From 940280028db1b28a4b5b2db712f62af2c851c8bf Mon Sep 17 00:00:00 2001 From: Peto Date: Wed, 10 Jul 2024 11:57:01 +0200 Subject: [PATCH 01/18] adjusted ordering of devices --- src/helpers/device-helpers.ts | 2 +- src/schema/api.graphql | 3 +-- src/schema/device.ts | 2 +- src/schema/nexus-typegen.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/helpers/device-helpers.ts b/src/helpers/device-helpers.ts index ffd604cd..61932bf6 100644 --- a/src/helpers/device-helpers.ts +++ b/src/helpers/device-helpers.ts @@ -13,7 +13,7 @@ type FilterQuery = { }; type DeviceOrderingInput = { - sortKey: 'name' | 'createdAt' | 'serviceState'; + sortKey: 'name' | 'discoveredAt'; direction: 'ASC' | 'DESC'; }; diff --git a/src/schema/api.graphql b/src/schema/api.graphql index c07fc17c..7bf561c4 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -654,9 +654,8 @@ type Snapshot { } enum SortDeviceBy { - createdAt + discoveredAt name - serviceState } enum SortDirection { diff --git a/src/schema/device.ts b/src/schema/device.ts index 7d53829e..d4c5b654 100644 --- a/src/schema/device.ts +++ b/src/schema/device.ts @@ -201,7 +201,7 @@ export const FilterDevicesInput = inputObjectType({ }); export const SortDeviceBy = enumType({ name: 'SortDeviceBy', - members: ['name', 'createdAt', 'serviceState'], + members: ['name', 'discoveredAt'], }); export const DeviceOrderByInput = inputObjectType({ name: 'DeviceOrderByInput', diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 70aabb50..1e77e02c 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -190,7 +190,7 @@ export interface NexusGenEnums { DeviceSize: 'LARGE' | 'MEDIUM' | 'SMALL'; DeviceSource: 'DISCOVERED' | 'IMPORTED' | 'MANUAL'; GraphEdgeStatus: 'ok' | 'unknown'; - SortDeviceBy: 'createdAt' | 'name' | 'serviceState'; + SortDeviceBy: 'discoveredAt' | 'name'; SortDirection: 'ASC' | 'DESC'; SortStreamBy: 'createdAt' | 'streamName'; TopologyLayer: 'EthTopology' | 'PhysicalTopology' | 'PtpTopology'; From 81e447a8d701c63d7c0a1b77c0970f0820d24a2c Mon Sep 17 00:00:00 2001 From: Peto Date: Wed, 10 Jul 2024 11:59:01 +0200 Subject: [PATCH 02/18] qRevert "adjusted ordering of devices" This reverts commit 940280028db1b28a4b5b2db712f62af2c851c8bf. --- src/helpers/device-helpers.ts | 2 +- src/schema/api.graphql | 3 ++- src/schema/device.ts | 2 +- src/schema/nexus-typegen.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/helpers/device-helpers.ts b/src/helpers/device-helpers.ts index 61932bf6..ffd604cd 100644 --- a/src/helpers/device-helpers.ts +++ b/src/helpers/device-helpers.ts @@ -13,7 +13,7 @@ type FilterQuery = { }; type DeviceOrderingInput = { - sortKey: 'name' | 'discoveredAt'; + sortKey: 'name' | 'createdAt' | 'serviceState'; direction: 'ASC' | 'DESC'; }; diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 7bf561c4..c07fc17c 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -654,8 +654,9 @@ type Snapshot { } enum SortDeviceBy { - discoveredAt + createdAt name + serviceState } enum SortDirection { diff --git a/src/schema/device.ts b/src/schema/device.ts index d4c5b654..7d53829e 100644 --- a/src/schema/device.ts +++ b/src/schema/device.ts @@ -201,7 +201,7 @@ export const FilterDevicesInput = inputObjectType({ }); export const SortDeviceBy = enumType({ name: 'SortDeviceBy', - members: ['name', 'discoveredAt'], + members: ['name', 'createdAt', 'serviceState'], }); export const DeviceOrderByInput = inputObjectType({ name: 'DeviceOrderByInput', diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 1e77e02c..70aabb50 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -190,7 +190,7 @@ export interface NexusGenEnums { DeviceSize: 'LARGE' | 'MEDIUM' | 'SMALL'; DeviceSource: 'DISCOVERED' | 'IMPORTED' | 'MANUAL'; GraphEdgeStatus: 'ok' | 'unknown'; - SortDeviceBy: 'discoveredAt' | 'name'; + SortDeviceBy: 'createdAt' | 'name' | 'serviceState'; SortDirection: 'ASC' | 'DESC'; SortStreamBy: 'createdAt' | 'streamName'; TopologyLayer: 'EthTopology' | 'PhysicalTopology' | 'PtpTopology'; From 8aca17eadcdf8e54a375186eca589571d7f1befb Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Fri, 26 Jul 2024 12:25:53 +0200 Subject: [PATCH 03/18] FR-201 add edit device location (#454) * create location with coordinates; add/edit device with location id * deviceMetadata endpoint added * add locationName to geomap data --- .../migration.sql | 2 + prisma/schema.prisma | 2 +- .../topology-discovery.graphql.ts | 182 ++++++++++++++++++ src/external-api/kafka.ts | 14 +- .../topology-discovery-graphql.ts | 33 ++++ src/helpers/device-helpers.ts | 6 +- src/helpers/id-helper.ts | 6 +- src/helpers/location-helpers.ts | 15 ++ src/helpers/topology.helpers.ts | 40 ++++ src/schema/api.graphql | 30 ++- src/schema/device.ts | 24 +-- src/schema/location.ts | 29 ++- src/schema/nexus-typegen.ts | 65 ++++++- src/schema/topology.ts | 53 +++++ 14 files changed, 466 insertions(+), 35 deletions(-) create mode 100644 prisma/migrations/20240718105212_location_country_optional/migration.sql create mode 100644 src/helpers/location-helpers.ts diff --git a/prisma/migrations/20240718105212_location_country_optional/migration.sql b/prisma/migrations/20240718105212_location_country_optional/migration.sql new file mode 100644 index 00000000..cfd92ff2 --- /dev/null +++ b/prisma/migrations/20240718105212_location_country_optional/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "location" ALTER COLUMN "country" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 207d6ddc..99528dca 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -99,7 +99,7 @@ model location { dependentLocality String? @map("dependent_locality") locality String? administrativeArea String? @map("administrative_area") - country String + country String? device device[] @@unique([name, tenantId], name: "udx_location_name_tenant_id") diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index 4e5dcd6e..9e03b805 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -130,6 +130,21 @@ export type InstalledDevices = { updated: Array; }; +/** LSP Tunnel (related to tunnel originating from this device). */ +export type LspTunnel = { + __typename?: 'LspTunnel'; + /** From which device is the tunnel originating. */ + from_device: Maybe; + /** Name of the link state packet. */ + lsp_id: Scalars['String']; + /** Type of signalisation. */ + signalisation: Signalisation; + /** Where is the tunnel headed. */ + to_device: Maybe; + /** Uptime of the tunnel in seconds. */ + uptime: Maybe; +}; + /** Grouped list of Metadata device objects and pagination metadata. */ export type MetadataConnection = { __typename?: 'MetadataConnection'; @@ -139,6 +154,150 @@ export type MetadataConnection = { pageInfo: PageInfo; }; +/** MPLS Data (related to all tunnels). */ +export type MplsData = { + __typename?: 'MplsData'; + /** The input interface. */ + input_interface: Maybe; + /** The input label. */ + input_label: Maybe; + /** Name of the link state packet. */ + lsp_id: Scalars['String']; + /** The input interface. */ + output_interface: Maybe; + /** The output label. */ + output_label: Maybe; +}; + +/** Representation of the device in the MPLS topology. */ +export type MplsDevice = Node & { + __typename?: 'MplsDevice'; + /** Coordinates of the device node on the graph. */ + coordinates: Coordinates; + /** Details of the device. */ + details: MplsDeviceDetails; + /** Unique identifier of the object. */ + id: Scalars['ID']; + /** List of strings that can be used for grouping of synced devices. */ + labels: Maybe>; + /** List of ports that are present on the device. */ + mplsInterfaces: MplsInterfaceConnection; + /** Human readable name of the device. */ + name: Scalars['String']; + /** Status of the device from the view of the synced topology. */ + status: NodeStatus; +}; + + +/** Representation of the device in the MPLS topology. */ +export type MplsDeviceMplsInterfacesArgs = { + cursor?: InputMaybe; + filters?: InputMaybe; + first?: InputMaybe; +}; + +/** Grouped list of MplsDevice objects and pagination metadata. */ +export type MplsDeviceConnection = { + __typename?: 'MplsDeviceConnection'; + /** List of MplsDevice objects. */ + edges: Maybe>>; + /** Pagination metadata. */ + pageInfo: PageInfo; +}; + +/** Details specific to MPLS (Multi-Protocol Label Switching). */ +export type MplsDeviceDetails = { + __typename?: 'MplsDeviceDetails'; + lsp_tunnels: Maybe>>; + mpls_data: Maybe>>; + router_id: Maybe; +}; + +/** Grouped MplsDevice object and associated cursor used by pagination. */ +export type MplsDeviceEdge = { + __typename?: 'MplsDeviceEdge'; + /** Pagination cursor for this edge. */ + cursor: Scalars['String']; + /** The associated MplsDevice object. */ + node: Maybe; +}; + +/** Filter for MplsDevice type based on device label and device name. */ +export type MplsDeviceFilter = { + /** Device label. */ + label?: InputMaybe; + /** Regex of device name. */ + name?: InputMaybe; +}; + +/** Port attached to the MPLS device. */ +export type MplsInterface = Node & { + __typename?: 'MplsInterface'; + /** Unique identifier of the object. */ + id: Scalars['ID']; + /** Device that owns this interface. */ + mplsDevice: Maybe; + /** Link to connected remote MPLS device. */ + mplsLinks: Maybe; + /** Human readable name of the network port. */ + name: Scalars['String']; + /** Status of the interface from the view of the synced topology ('ok' or 'unknown'). */ + status: NodeStatus; +}; + + +/** Port attached to the MPLS device. */ +export type MplsInterfaceMplsLinksArgs = { + cursor?: InputMaybe; + first?: InputMaybe; +}; + +/** Grouped list of MplsInterface objects and pagination metadata. */ +export type MplsInterfaceConnection = { + __typename?: 'MplsInterfaceConnection'; + /** List of MplsInterface objects. */ + edges: Maybe>>; + /** Pagination metadata. */ + pageInfo: PageInfo; +}; + +/** Grouped MplsInterface object and associated cursor used by pagination. */ +export type MplsInterfaceEdge = { + __typename?: 'MplsInterfaceEdge'; + /** Pagination cursor for this edge. */ + cursor: Scalars['String']; + /** The associated MplsInterface object. */ + node: Maybe; +}; + +/** Filter for MplsInterface type based on the current interface status and name of the device. */ +export type MplsInterfaceFilter = { + /** Regex of interface name. */ + name?: InputMaybe; + /** Status of the interface from the view of the synced topology. */ + status?: InputMaybe; +}; + +/** Grouped list of MplsLinks objects and pagination metadata. */ +export type MplsLinkConnection = { + __typename?: 'MplsLinkConnection'; + /** List of MplsInterface objects. */ + edges: Maybe>>; + /** Pagination metadata. */ + pageInfo: PageInfo; +}; + +/** Grouped MplsLink object and associated cursor used by pagination. */ +export type MplsLinkEdge = { + __typename?: 'MplsLinkEdge'; + /** Pagination cursor for this edge. */ + cursor: Scalars['String']; + /** Identifier of the link that connects this interface to the interface on the remote device */ + link: Maybe; + /** The associated MplsInterface object. */ + node: Maybe; +}; + export type Mutation = { __typename?: 'Mutation'; /** @@ -629,6 +788,8 @@ export type PtpDeviceDetails = { gm_clock_id: Maybe; /** Unique identifier of the parent clock. */ parent_clock_id: Maybe; + /** The port state of the device. */ + ptp_port_state: Maybe; /** PTP profile used (e.g., ITU-T G.8275.1). */ ptp_profile: Maybe; /** @@ -832,6 +993,8 @@ export type Query = { commonNodes: CommonNodesResponse; /** Read devices that match specified filter. */ deviceMetadata: MetadataConnection; + /** Read MPLS devices that match the specified filter. */ + mplsDevices: MplsDeviceConnection; /** Read network devices that match specified filter. */ netDevices: NetDeviceConnection; /** @@ -896,6 +1059,13 @@ export type QueryDeviceMetadataArgs = { }; +export type QueryMplsDevicesArgs = { + cursor?: InputMaybe; + filters?: InputMaybe; + first?: InputMaybe; +}; + + export type QueryNetDevicesArgs = { cursor?: InputMaybe; filters?: InputMaybe; @@ -975,6 +1145,10 @@ export type RoutingPath = { weight: Scalars['Int']; }; +export type Signalisation = + | 'LDP' + | 'RSVP'; + /** Response from the sync query that contains information about synced devices from the network to topology. */ export type SyncResponse = { __typename?: 'SyncResponse'; @@ -1214,6 +1388,7 @@ export type TopologyResponse = { /** Present topology types. */ export type TopologyType = | 'EthTopology' + | 'MplsTopology' | 'NetworkTopology' | 'PhysicalTopology' | 'PtpTopology'; @@ -1306,3 +1481,10 @@ export type SyncePathToGrandMasterQueryVariables = Exact<{ export type SyncePathToGrandMasterQuery = { __typename?: 'Query', syncePathToGm: { __typename?: 'SyncePath', nodes: Array | null } }; + +export type DeviceMetadataQueryVariables = Exact<{ + filters?: InputMaybe; +}>; + + +export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; diff --git a/src/external-api/kafka.ts b/src/external-api/kafka.ts index 8eaa00ec..76b99bb8 100644 --- a/src/external-api/kafka.ts +++ b/src/external-api/kafka.ts @@ -1,11 +1,13 @@ import { Consumer, Kafka, Producer } from 'kafkajs'; import config from '../config'; import { Device } from '../schema/source-types'; -import { encodeDeviceForInventoryKafka } from '../helpers/device-helpers'; +import { DeviceLocation, encodeDeviceForInventoryKafka } from '../helpers/device-helpers'; const KAFKA_BROKER = config.kafkaBroker || ''; const KAFKA_TOPIC = config.kafkaTopic || ''; +type Coordinates = [number, number]; + class KafkaService { private kafka: Kafka; @@ -103,7 +105,7 @@ class KafkaService { async function produceDeviceRegistrationEvent( kafka: Omit | null, device: Device, - coordinates: [number, number], + coordinates: Coordinates | null, labelIds: string[], ): Promise { if (kafka == null) { @@ -111,7 +113,8 @@ async function produceDeviceRegistrationEvent( } try { - await kafka.send(device.name, encodeDeviceForInventoryKafka(device, { type: 'Point', coordinates }, labelIds), { + const kafkaCoordinates: DeviceLocation | null = coordinates ? { type: 'Point', coordinates } : null; + await kafka.send(device.name, encodeDeviceForInventoryKafka(device, kafkaCoordinates, labelIds), { type: 'device_registration', }); } catch (error) { @@ -142,7 +145,7 @@ async function produceDeviceRemovalEvent( async function produceDeviceUpdateEvent( kafka: Omit | null, device: Device, - coordinates: [number, number], + coordinates: Coordinates | null, labelIds: string[], ): Promise { if (kafka == null) { @@ -150,7 +153,8 @@ async function produceDeviceUpdateEvent( } try { - await kafka.send(device.name, encodeDeviceForInventoryKafka(device, { type: 'Point', coordinates }, labelIds), { + const kafkaCoordinates: DeviceLocation | null = coordinates ? { type: 'Point', coordinates } : null; + await kafka.send(device.name, encodeDeviceForInventoryKafka(device, kafkaCoordinates, labelIds), { type: 'device_update', }); } catch (error) { diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 363c6423..8976ee47 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -21,6 +21,8 @@ import { SynceTopologyQuery, SyncePathToGrandMasterQuery, SyncePathToGrandMasterQueryVariables, + DeviceMetadataQuery, + DeviceMetadataQueryVariables, } from '../__generated__/topology-discovery.graphql'; import { TopologyDiffOutput, decodeTopologyDiffOutput } from './topology-network-types'; @@ -408,6 +410,29 @@ const SYNCE_PATH = gql` } `; +const DEVICE_METADATA = gql` + query DeviceMetadata($filters: DeviceMetadataFilter) { + deviceMetadata(filters: $filters) { + edges { + node { + id + deviceName + deviceType + model + vendor + version + protocolType + geoLocation { + bbox + coordinates + type + } + } + } + } + } +`; + function getTopologyDiscoveryApi() { if (!config.topologyEnabled) { return undefined; @@ -521,6 +546,13 @@ function getTopologyDiscoveryApi() { return response.syncePathToGm.nodes; } + async function getDeviceMetadata(): Promise { + const response = await client.request(DEVICE_METADATA, { + filters: undefined, + }); + return response; + } + return { getTopologyDevices, getPtpDiffSynce, @@ -534,6 +566,7 @@ function getTopologyDiscoveryApi() { updateCoordinates, getSynceTopology, getSyncePathToGrandMaster, + getDeviceMetadata, }; } diff --git a/src/helpers/device-helpers.ts b/src/helpers/device-helpers.ts index ae44657b..75aa2ba7 100644 --- a/src/helpers/device-helpers.ts +++ b/src/helpers/device-helpers.ts @@ -90,14 +90,14 @@ export function makeZonesWithDevicesFromDevices(devices: Device[]) { return zonesWithDevices; } -type DeviceLocation = { +export type DeviceLocation = { type: 'Point'; coordinates: [number, number]; }; export function encodeDeviceForInventoryKafka( device: Device, - deviceLocation: DeviceLocation, + deviceLocation: DeviceLocation | null, deviceLabelsIds: string[] = [], ) { return { @@ -127,6 +127,6 @@ export function encodeDeviceForInventoryKafka( // eslint-disable-next-line @typescript-eslint/naming-convention labels_ids: deviceLabelsIds, // eslint-disable-next-line @typescript-eslint/naming-convention - geo_location: deviceLocation, + geo_location: deviceLocation ?? undefined, }; } diff --git a/src/helpers/id-helper.ts b/src/helpers/id-helper.ts index 41c44e7f..0dcdb306 100644 --- a/src/helpers/id-helper.ts +++ b/src/helpers/id-helper.ts @@ -9,7 +9,8 @@ export type DataType = | 'Country' | 'Blueprint' | 'GraphNode' - | 'GraphEdge'; + | 'GraphEdge' + | 'MapNode'; function isDataType(value: string): value is DataType { return ( @@ -21,7 +22,8 @@ function isDataType(value: string): value is DataType { value === 'Country' || value === 'Blueprint' || value === 'GraphNode' || - value === 'GraphEdge' + value === 'GraphEdge' || + value === 'MapNode' ); } diff --git a/src/helpers/location-helpers.ts b/src/helpers/location-helpers.ts new file mode 100644 index 00000000..bb34809e --- /dev/null +++ b/src/helpers/location-helpers.ts @@ -0,0 +1,15 @@ +import countries from 'i18n-iso-countries'; +import { fromGraphId } from './id-helper'; + +export function getCountryName(countryId: string | null): string | null { + if (countryId) { + const countryCode = fromGraphId('Country', countryId); + if (!countries.isValid(countryCode)) { + throw new Error('invalid countryId'); + } + const countryName = countries.getName(countryCode, 'en', { select: 'official' }); + return countryName; + } + + return null; +} diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index 7a027c39..9c0420da 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -1,5 +1,6 @@ import { device as PrismaDevice } from '@prisma/client'; import { + DeviceMetadataQuery, NetTopologyQuery, PhyDevice, PtpDevice, @@ -28,6 +29,7 @@ import { import { omitNullValue, unwrap } from './utils.helpers'; import { toGraphId } from './id-helper'; import { NexusGenObjects } from '../schema/nexus-typegen'; +import { Location } from '../schema/source-types'; type FilterInput = { labelIds?: string[] | null; @@ -1542,3 +1544,41 @@ export function makeNetTopologyDiff( edges: oldEdges, }; } + +export function convertDeviceMetadataToMapNodes( + deviceMetadataQuery: DeviceMetadataQuery, + deviceLocationMap: Map, +) { + if (deviceMetadataQuery.deviceMetadata?.edges == null) { + return null; + } + + const mapNodes = deviceMetadataQuery.deviceMetadata.edges + .filter(omitNullValue) + .map((e) => { + const { node } = e; + if (node == null) { + return null; + } + + const { id, deviceName, geoLocation: topologyGeolocation } = node; + const geolocation = topologyGeolocation + ? { + latitude: topologyGeolocation.coordinates[0], + longitude: topologyGeolocation.coordinates[1], + } + : null; + + const deviceLocation = deviceLocationMap.get(node.deviceName)?.name ?? null; + + return { + id: toGraphId('MapNode', id), + deviceName, + locationName: deviceLocation, + geolocation, + }; + }) + .filter(omitNullValue); + + return mapNodes; +} diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 9f2ae2a0..5ce0266e 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -20,6 +20,7 @@ input AddDeviceInput { deviceSize: DeviceSize deviceType: String labelIds: [String!] + locationId: String model: String mountParameters: String name: String! @@ -37,7 +38,8 @@ type AddDevicePayload { } input AddLocationInput { - countryId: String! + coordinates: Coordinates! + countryId: String name: String! } @@ -185,6 +187,11 @@ type CommitConfigPayload { output: CommitConfigOutput! } +input Coordinates { + latitude: Float! + longitude: Float! +} + type Country implements Node { code: String! id: ID! @@ -293,6 +300,10 @@ type DeviceListUsage { devicesUsage: [DevicesUsage!]! } +type DeviceMetadata { + nodes: [GeoMapDevice] +} + input DeviceOrderByInput { direction: SortDirection! sortKey: SortDeviceBy! @@ -369,6 +380,18 @@ input FilterZonesInput { name: String! } +type GeoMapDevice { + deviceName: String! + geolocation: Geolocation + id: ID! + locationName: String +} + +type Geolocation { + latitude: Float! + longitude: Float! +} + type GraphEdge { id: ID! source: EdgeSourceTarget! @@ -450,9 +473,11 @@ type LabelEdge { } type Location implements Node { - country: String! + country: String createdAt: String! id: ID! + latitude: Float + longitude: Float name: String! updatedAt: String! } @@ -628,6 +653,7 @@ type Query { calculatedDiff(deviceId: String!, transactionId: String!): CalculatedDiffPayload! countries(after: String, before: String, first: Int, last: Int): CountryConnection! dataStore(deviceId: String!, transactionId: String!): DataStore + deviceMetadata: DeviceMetadata devices( after: String before: String diff --git a/src/schema/device.ts b/src/schema/device.ts index 5814aa23..50c36ac8 100644 --- a/src/schema/device.ts +++ b/src/schema/device.ts @@ -267,6 +267,7 @@ export const AddDeviceInput = inputObjectType({ t.int('port'); t.string('deviceType'); t.string('version'); + t.string('locationId'); }, }); export const AddDevicePayload = objectType({ @@ -322,14 +323,13 @@ export const AddDeviceMutation = extendType({ const deviceLocation = await prisma.location.findFirst({ where: { id: device.locationId ?? undefined }, }); + const geoLocation: [number, number] | null = + deviceLocation?.latitude && deviceLocation?.longitude + ? [Number.parseFloat(deviceLocation.latitude ?? '0'), Number.parseFloat(deviceLocation.longitude ?? '0')] + : null; if (config.kafkaEnabled) { - await inventoryKafka?.produceDeviceRegistrationEvent( - kafka, - device, - [Number.parseFloat(deviceLocation?.latitude ?? '0'), Number.parseFloat(deviceLocation?.longitude ?? '0')], - labelIds ?? [], - ); + await inventoryKafka?.produceDeviceRegistrationEvent(kafka, device, geoLocation, labelIds ?? []); } return { device }; @@ -443,13 +443,13 @@ export const UpdateDeviceMutation = extendType({ where: { id: updatedDevice.locationId ?? undefined }, }); + const geoLocation: [number, number] | null = + deviceLocation?.latitude && deviceLocation?.longitude + ? [Number.parseFloat(deviceLocation.latitude ?? '0'), Number.parseFloat(deviceLocation.longitude ?? '0')] + : null; + if (config.kafkaEnabled) { - await inventoryKafka?.produceDeviceUpdateEvent( - kafka, - updatedDevice, - [Number.parseFloat(deviceLocation?.latitude ?? '0'), Number.parseFloat(deviceLocation?.longitude ?? '0')], - labelIds, - ); + await inventoryKafka?.produceDeviceUpdateEvent(kafka, updatedDevice, geoLocation, labelIds); } return { device: updatedDevice }; diff --git a/src/schema/location.ts b/src/schema/location.ts index e1dfcd63..de19b0ec 100644 --- a/src/schema/location.ts +++ b/src/schema/location.ts @@ -2,8 +2,9 @@ import { findManyCursorConnection } from '@devoxa/prisma-relay-cursor-connection import { connectionFromArray } from 'graphql-relay'; import countries from 'i18n-iso-countries'; import { arg, extendType, inputObjectType, nonNull, objectType } from 'nexus'; -import { fromGraphId, toGraphId } from '../helpers/id-helper'; +import { toGraphId } from '../helpers/id-helper'; import { Node, PageInfo, PaginationConnectionArgs } from './global-types'; +import { getCountryName } from '../helpers/location-helpers'; export const Location = objectType({ name: 'Location', @@ -19,7 +20,9 @@ export const Location = objectType({ t.nonNull.string('updatedAt', { resolve: (root) => root.updatedAt.toISOString(), }); - t.nonNull.string('country'); + t.string('country'); + t.float('latitude'); + t.float('longitude'); }, }); @@ -116,11 +119,21 @@ export const AddLocationPayload = objectType({ t.nonNull.field('location', { type: Location }); }, }); + +export const Coordinates = inputObjectType({ + name: 'Coordinates', + definition: (t) => { + t.nonNull.float('latitude'); + t.nonNull.float('longitude'); + }, +}); + export const AddLocationInput = inputObjectType({ name: 'AddLocationInput', definition: (t) => { t.nonNull.string('name'); - t.nonNull.string('countryId'); + t.string('countryId'); + t.nonNull.field({ name: 'coordinates', type: Coordinates }); }, }); export const AddLocationMutation = extendType({ @@ -133,16 +146,16 @@ export const AddLocationMutation = extendType({ }, resolve: async (_, args, { prisma, tenantId }) => { const { input } = args; - const countryCode = fromGraphId('Country', input.countryId); - if (!countries.isValid(countryCode)) { - throw new Error('invalid countryId'); - } - const countryName = countries.getName(countryCode, 'en', { select: 'official' }); + + const countryName = getCountryName(input.countryId ?? null); + const location = await prisma.location.create({ data: { tenantId, name: input.name, country: countryName, + latitude: input.coordinates.latitude.toString(), + longitude: input.coordinates.latitude.toString(), }, }); return { diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 7ebd92b2..e1d209e4 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -43,6 +43,7 @@ export interface NexusGenInputs { deviceSize?: NexusGenEnums['DeviceSize'] | null; // DeviceSize deviceType?: string | null; // String labelIds?: string[] | null; // [String!] + locationId?: string | null; // String model?: string | null; // String mountParameters?: string | null; // String name: string; // String! @@ -56,7 +57,8 @@ export interface NexusGenInputs { }; AddLocationInput: { // input type - countryId: string; // String! + coordinates: NexusGenInputs['Coordinates']; // Coordinates! + countryId?: string | null; // String name: string; // String! }; AddSnapshotInput: { @@ -106,6 +108,11 @@ export interface NexusGenInputs { deviceId: string; // String! shouldDryRun?: boolean | null; // Boolean }; + Coordinates: { + // input type + latitude: number; // Float! + longitude: number; // Float! + }; CreateLabelInput: { // input type name: string; // String! @@ -376,6 +383,10 @@ export interface NexusGenObjects { // root type devicesUsage: NexusGenRootTypes['DevicesUsage'][]; // [DevicesUsage!]! }; + DeviceMetadata: { + // root type + nodes?: Array | null; // [GeoMapDevice] + }; DeviceStatus: { // root type deviceName?: string | null; // String @@ -406,6 +417,18 @@ export interface NexusGenObjects { interface: string; // String! nodeId: string; // String! }; + GeoMapDevice: { + // root type + deviceName: string; // String! + geolocation?: NexusGenRootTypes['Geolocation'] | null; // Geolocation + id: string; // ID! + locationName?: string | null; // String + }; + Geolocation: { + // root type + latitude: number; // Float! + longitude: number; // Float! + }; GraphEdge: { // root type id: string; // ID! @@ -952,6 +975,10 @@ export interface NexusGenFieldTypes { // field return type devicesUsage: NexusGenRootTypes['DevicesUsage'][]; // [DevicesUsage!]! }; + DeviceMetadata: { + // field return type + nodes: Array | null; // [GeoMapDevice] + }; DeviceStatus: { // field return type deviceName: string | null; // String @@ -982,6 +1009,18 @@ export interface NexusGenFieldTypes { interface: string; // String! nodeId: string; // String! }; + GeoMapDevice: { + // field return type + deviceName: string; // String! + geolocation: NexusGenRootTypes['Geolocation'] | null; // Geolocation + id: string; // ID! + locationName: string | null; // String + }; + Geolocation: { + // field return type + latitude: number; // Float! + longitude: number; // Float! + }; GraphEdge: { // field return type id: string; // ID! @@ -1053,9 +1092,11 @@ export interface NexusGenFieldTypes { }; Location: { // field return type - country: string; // String! + country: string | null; // String createdAt: string; // String! id: string; // ID! + latitude: number | null; // Float + longitude: number | null; // Float name: string; // String! updatedAt: string; // String! }; @@ -1227,6 +1268,7 @@ export interface NexusGenFieldTypes { calculatedDiff: NexusGenRootTypes['CalculatedDiffPayload']; // CalculatedDiffPayload! countries: NexusGenRootTypes['CountryConnection']; // CountryConnection! dataStore: NexusGenRootTypes['DataStore'] | null; // DataStore + deviceMetadata: NexusGenRootTypes['DeviceMetadata'] | null; // DeviceMetadata devices: NexusGenRootTypes['DeviceConnection']; // DeviceConnection! kafkaHealthCheck: NexusGenRootTypes['IsOkResponse'] | null; // IsOkResponse labels: NexusGenRootTypes['LabelConnection']; // LabelConnection! @@ -1623,6 +1665,10 @@ export interface NexusGenFieldTypeNames { // field return type name devicesUsage: 'DevicesUsage'; }; + DeviceMetadata: { + // field return type name + nodes: 'GeoMapDevice'; + }; DeviceStatus: { // field return type name deviceName: 'String'; @@ -1653,6 +1699,18 @@ export interface NexusGenFieldTypeNames { interface: 'String'; nodeId: 'String'; }; + GeoMapDevice: { + // field return type name + deviceName: 'String'; + geolocation: 'Geolocation'; + id: 'ID'; + locationName: 'String'; + }; + Geolocation: { + // field return type name + latitude: 'Float'; + longitude: 'Float'; + }; GraphEdge: { // field return type name id: 'ID'; @@ -1727,6 +1785,8 @@ export interface NexusGenFieldTypeNames { country: 'String'; createdAt: 'String'; id: 'ID'; + latitude: 'Float'; + longitude: 'Float'; name: 'String'; updatedAt: 'String'; }; @@ -1898,6 +1958,7 @@ export interface NexusGenFieldTypeNames { calculatedDiff: 'CalculatedDiffPayload'; countries: 'CountryConnection'; dataStore: 'DataStore'; + deviceMetadata: 'DeviceMetadata'; devices: 'DeviceConnection'; kafkaHealthCheck: 'IsOkResponse'; labels: 'LabelConnection'; diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 6a226430..9ac98118 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -12,6 +12,7 @@ import { import config from '../config'; import { fromGraphId } from '../helpers/id-helper'; import { + convertDeviceMetadataToMapNodes, getDeviceInterfaceEdges, getEdgesFromTopologyQuery, getFilterQuery, @@ -113,6 +114,24 @@ export const GraphNode = objectType({ }, }); +export const Geolocation = objectType({ + name: 'Geolocation', + definition: (t) => { + t.nonNull.float('latitude'); + t.nonNull.float('longitude'); + }, +}); + +export const GeoMapDevice = objectType({ + name: 'GeoMapDevice', + definition: (t) => { + t.nonNull.id('id'); + t.nonNull.string('deviceName'); + t.string('locationName'); + t.field('geolocation', { type: Geolocation }); + }, +}); + export const EdgeSourceTarget = objectType({ name: 'EdgeSourceTarget', definition: (t) => { @@ -761,3 +780,37 @@ export const SyncePathToGrandMasterQuery = queryField('syncePathToGrandMaster', return syncePathResult ?? []; }, }); + +export const DeviceMetadata = objectType({ + name: 'DeviceMetadata', + definition: (t) => { + t.list.field('nodes', { + type: GeoMapDevice, + }); + }, +}); + +export const deviceMetadataQuery = queryField('deviceMetadata', { + type: DeviceMetadata, + resolve: async (_, args, { prisma, topologyDiscoveryGraphQLAPI }) => { + if (!topologyDiscoveryGraphQLAPI) { + return null; + } + const deviceMetadataResult = await topologyDiscoveryGraphQLAPI.getDeviceMetadata(); + + const dbDevices = await prisma.device.findMany({ + include: { + location: true, + }, + }); + + const deviceLocationMap = new Map(dbDevices.map((d) => [d.name, d.location])); + + const mapNodes = convertDeviceMetadataToMapNodes(deviceMetadataResult, deviceLocationMap); + return mapNodes + ? { + nodes: mapNodes, + } + : null; + }, +}); From 54998879d7dff45d6d96d14de99b8d3b662b1fb5 Mon Sep 17 00:00:00 2001 From: plehocky <117287338+plehocky@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:47:23 +0200 Subject: [PATCH 04/18] fixed locationId to be stored on creating device (#457) --- src/schema/device.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/schema/device.ts b/src/schema/device.ts index 50c36ac8..f7af0323 100644 --- a/src/schema/device.ts +++ b/src/schema/device.ts @@ -288,6 +288,10 @@ export const AddDeviceMutation = extendType({ const { input } = args; const nativeZoneId = fromGraphId('Zone', input.zoneId); const zone = await prisma.uniconfigZone.findFirst({ where: { tenantId, id: nativeZoneId } }); + const deviceLocation = await prisma.location.findFirst({ + where: { id: input.locationId ?? undefined }, + }); + if (zone == null) { throw new Error('zone not found'); } @@ -307,6 +311,7 @@ export const AddDeviceMutation = extendType({ port: input.port ?? undefined, deviceType: input.deviceType, version: input.version, + locationId: input.locationId ? fromGraphId('Location', input.locationId) : undefined, mountParameters: input.mountParameters != null ? JSON.parse(input.mountParameters) : undefined, source: 'MANUAL', serviceState: input.serviceState ?? undefined, @@ -320,9 +325,6 @@ export const AddDeviceMutation = extendType({ }, }); - const deviceLocation = await prisma.location.findFirst({ - where: { id: device.locationId ?? undefined }, - }); const geoLocation: [number, number] | null = deviceLocation?.latitude && deviceLocation?.longitude ? [Number.parseFloat(deviceLocation.latitude ?? '0'), Number.parseFloat(deviceLocation.longitude ?? '0')] From e0b618cdc8de472de50bc8d1b4c17527d4839d7f Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Wed, 7 Aug 2024 10:34:25 +0200 Subject: [PATCH 05/18] FR-235 start stop time (#453) (#458) * stream start/stop activation time added * migration added * add startedAt/stoppedAt to stream * lint fix --- .../migration.sql | 3 +++ prisma/schema.prisma | 2 ++ src/schema/api.graphql | 2 ++ src/schema/nexus-typegen.ts | 4 ++++ src/schema/stream.ts | 21 +++++++++++++++++++ 5 files changed, 32 insertions(+) create mode 100644 prisma/migrations/20240722092527_add_stream_start_stop_time/migration.sql diff --git a/prisma/migrations/20240722092527_add_stream_start_stop_time/migration.sql b/prisma/migrations/20240722092527_add_stream_start_stop_time/migration.sql new file mode 100644 index 00000000..8fecac6c --- /dev/null +++ b/prisma/migrations/20240722092527_add_stream_start_stop_time/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "stream" ADD COLUMN "started_at" TIMESTAMP(3), +ADD COLUMN "stopped_at" TIMESTAMP(3); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 99528dca..ab44f7ec 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -125,6 +125,8 @@ model stream { id String @id @default(uuid()) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") + startedAt DateTime? @map("started_at") + stoppedAt DateTime? @map("stopped_at") deviceName String @map("device_name") streamName String @map("stream_name") streamParameters Json? @map("stream_parameters") diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 5ce0266e..3c7331a2 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -729,6 +729,8 @@ type Stream implements Node { deviceName: String! id: ID! isActive: Boolean! + startedAt: String + stoppedAt: String streamName: String! streamParameters: String updatedAt: String! diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index e1d209e4..e50be399 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -1313,6 +1313,8 @@ export interface NexusGenFieldTypes { deviceName: string; // String! id: string; // ID! isActive: boolean; // Boolean! + startedAt: string | null; // String + stoppedAt: string | null; // String streamName: string; // String! streamParameters: string | null; // String updatedAt: string; // String! @@ -2003,6 +2005,8 @@ export interface NexusGenFieldTypeNames { deviceName: 'String'; id: 'ID'; isActive: 'Boolean'; + startedAt: 'String'; + stoppedAt: 'String'; streamName: 'String'; streamParameters: 'String'; updatedAt: 'String'; diff --git a/src/schema/stream.ts b/src/schema/stream.ts index 07c38b47..34f11eb0 100644 --- a/src/schema/stream.ts +++ b/src/schema/stream.ts @@ -39,6 +39,12 @@ export const StreamNode = objectType({ t.nonNull.string('updatedAt', { resolve: (stream) => stream.updatedAt.toISOString(), }); + t.string('startedAt', { + resolve: (stream) => stream.startedAt?.toISOString() || null, + }); + t.string('stoppedAt', { + resolve: (stream) => stream.stoppedAt?.toISOString() || null, + }); t.nonNull.string('streamName'); t.nonNull.string('deviceName'); t.nonNull.boolean('isActive', { @@ -245,6 +251,14 @@ export const ActivateStreamMutation = extendType({ const uniconfigURL = await getUniconfigURL(prisma, device.uniconfigZoneId); await installDeviceCache({ uniconfigURL, deviceName: uniconfigStreamName, params: installStreamParams }); + await prisma.stream.update({ + where: { id: nativeId }, + data: { + startedAt: new Date(), + stoppedAt: null, + }, + }); + return { stream }; }, }); @@ -291,6 +305,13 @@ export const DeactivateStreamMutation = extendType({ params: uninstallParams, deviceName: getUniconfigStreamName(stream.streamName, stream.deviceName), }); + await prisma.stream.update({ + where: { id: nativeId }, + data: { + stoppedAt: new Date(), + }, + }); + return { stream }; }, }); From 693334703ee21776d7acd05825278850e028aeab Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Wed, 7 Aug 2024 11:40:12 +0200 Subject: [PATCH 06/18] Merge mpls (#459) * FR-235 start stop time (#453) * stream start/stop activation time added * migration added * add startedAt/stoppedAt to stream * lint fix * FR-306 mpls topology endpoint added (#456) * mpls topology endpoint added * mpls topology added to topology layer --- .../topology-discovery.graphql.ts | 31 ++++ .../topology-discovery-graphql.ts | 101 +++++++++++++ src/helpers/topology.helpers.ts | 116 ++++++++++++++- src/schema/api.graphql | 50 +++++++ src/schema/nexus-typegen.ts | 134 +++++++++++++++++- src/schema/topology.ts | 85 ++++++++++- 6 files changed, 514 insertions(+), 3 deletions(-) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index 9e03b805..3a73e09c 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -298,6 +298,16 @@ export type MplsLinkEdge = { node: Maybe; }; +export type MplsTotalLsps = { + __typename?: 'MplsTotalLsps'; + /** Number of incoming LSPs. */ + incoming_lsps: Maybe; + /** Number of outcoming LSPs. */ + outcoming_lsps: Maybe; + /** To which device the LSP is headed. */ + to_device: Maybe; +}; + export type Mutation = { __typename?: 'Mutation'; /** @@ -995,6 +1005,11 @@ export type Query = { deviceMetadata: MetadataConnection; /** Read MPLS devices that match the specified filter. */ mplsDevices: MplsDeviceConnection; + /** + * Returns a list of LSPs based on to which device they are headed. + * Also return the count of incoming and outcoming tunnels from / to that device. + */ + mplsLspCount: Maybe>>; /** Read network devices that match specified filter. */ netDevices: NetDeviceConnection; /** @@ -1066,6 +1081,11 @@ export type QueryMplsDevicesArgs = { }; +export type QueryMplsLspCountArgs = { + device_id: Scalars['ID']; +}; + + export type QueryNetDevicesArgs = { cursor?: InputMaybe; filters?: InputMaybe; @@ -1488,3 +1508,14 @@ export type DeviceMetadataQueryVariables = Exact<{ export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; + +export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, input_label: number | null, input_interface: string | null, output_interface: string | null, output_label: number | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; + +export type MplsInterfaceDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } }; + +export type MplsInterfacePartsFragment = { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null }; + +export type MplsTopologyQueryVariables = Exact<{ [key: string]: never; }>; + + +export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, input_label: number | null, input_interface: string | null, output_interface: string | null, output_label: number | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 8976ee47..4b647ff2 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -23,6 +23,7 @@ import { SyncePathToGrandMasterQueryVariables, DeviceMetadataQuery, DeviceMetadataQueryVariables, + MplsTopologyQuery, } from '../__generated__/topology-discovery.graphql'; import { TopologyDiffOutput, decodeTopologyDiffOutput } from './topology-network-types'; @@ -433,6 +434,99 @@ const DEVICE_METADATA = gql` } `; +const MPLS_TOPOLOGY = gql` + fragment MplsDeviceParts on MplsDevice { + id + name + status + labels + coordinates { + x + y + } + mplsInterfaces { + edges { + node { + ...MplsInterfaceParts + } + } + } + details { + mpls_data { + lsp_id + input_label + input_interface + output_interface + output_label + } + lsp_tunnels { + lsp_id + from_device + to_device + signalisation + uptime + } + } + } + + fragment MplsInterfaceDeviceParts on MplsDevice { + id + name + coordinates { + x + y + } + mplsInterfaces { + edges { + node { + id + name + mplsLinks { + edges { + link + node { + id + name + } + } + } + } + } + } + } + + fragment MplsInterfaceParts on MplsInterface { + id + name + status + mplsDevice { + ...MplsInterfaceDeviceParts + } + mplsLinks { + edges { + link + node { + id + mplsDevice { + ...MplsInterfaceDeviceParts + } + } + } + } + } + + query MplsTopology { + mplsDevices { + edges { + cursor + node { + ...MplsDeviceParts + } + } + } + } +`; + function getTopologyDiscoveryApi() { if (!config.topologyEnabled) { return undefined; @@ -553,6 +647,12 @@ function getTopologyDiscoveryApi() { return response; } + async function getMplsTopology(): Promise { + const response = await client.request(MPLS_TOPOLOGY); + + return response; + } + return { getTopologyDevices, getPtpDiffSynce, @@ -567,6 +667,7 @@ function getTopologyDiscoveryApi() { getSynceTopology, getSyncePathToGrandMaster, getDeviceMetadata, + getMplsTopology, }; } diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index 9c0420da..ffd13258 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -1,9 +1,11 @@ import { device as PrismaDevice } from '@prisma/client'; import { DeviceMetadataQuery, + MplsTopologyQuery, NetTopologyQuery, PhyDevice, PtpDevice, + PtpDeviceDetails as ApiPtpDeviceDetails, PtpTopologyQuery, SynceDevice, SynceTopologyQuery, @@ -99,9 +101,12 @@ export function getFilterQuery(filter?: FilterInput | null): FilterQuery | undef }; } +type CustomPtpDetails = Omit; type QueryNetDevice = NonNullable[0]>['node']>; type PhyDeviceWithoutInterfaces = Omit; -type PtpDeviceWithoutInterfaces = Omit; +type PtpDeviceWithoutInterfaces = Omit & { + details: CustomPtpDetails; +}; type SynceDeviceWithoutInterfaces = Omit; export function convertNetDeviceToArangoDevice(netDevice: QueryNetDevice): ArangoNetDevice | null { @@ -1582,3 +1587,112 @@ export function convertDeviceMetadataToMapNodes( return mapNodes; } + +export function makeMplsTopologyNodes(mplsDevices?: MplsTopologyQuery) { + return ( + mplsDevices?.mplsDevices.edges + ?.map((e) => { + const node = e?.node; + if (!node) { + return null; + } + return { + id: toGraphId('GraphNode', node.id), + nodeId: node.id, + name: node.name, + status: getStatus(node.status), + labels: node.labels?.map((l) => l) ?? [], + coordinates: node.coordinates, + interfaces: + node.mplsInterfaces.edges + ?.map((i) => { + const interfaceNode = i?.node; + if (!interfaceNode) { + return null; + } + return { + id: interfaceNode.id, + name: interfaceNode.name, + status: getStatus(interfaceNode.status), + }; + }) + .filter(omitNullValue) ?? [], + mplsDeviceDetails: { + lspTunnels: + node.details.lsp_tunnels?.map((tunnel) => ({ + lspId: tunnel?.lsp_id ?? '', + fromDevice: tunnel?.from_device ?? null, + toDevice: tunnel?.to_device ?? null, + uptime: tunnel?.uptime ?? null, + signalization: tunnel?.signalisation ?? null, + })) ?? null, + mplsData: + node.details.mpls_data?.map((d) => ({ + lspId: d?.lsp_id ?? '', + inputLabel: d?.input_label ?? null, + inputInterface: d?.input_interface ?? null, + outputInterface: d?.output_interface ?? null, + outputLabel: d?.output_label ?? null, + })) ?? null, + }, + }; + }) + .filter(omitNullValue) ?? [] + ); +} + +export function getMplsTopologyInterfaces(topologyDevices: MplsTopologyQuery) { + return ( + topologyDevices.mplsDevices.edges?.flatMap((e) => { + const node = e?.node; + if (!node) { + return []; + } + const nodeInterfaces = node.mplsInterfaces.edges + ?.map((ie) => { + const inode = ie?.node; + if (!inode || !inode.mplsLinks) { + return null; + } + return { + ...inode, + _id: inode.id, + nodeId: node.name, + }; + }) + .filter(omitNullValue); + return nodeInterfaces || []; + }) ?? [] + ); +} + +export function makeMplsTopologyEdges(mplsDevices?: MplsTopologyQuery) { + if (!mplsDevices) { + return []; + } + + return getMplsTopologyInterfaces(mplsDevices).flatMap((i) => { + const links = + i.mplsLinks?.edges + ?.map((mplsLinkEdge) => { + if (!mplsLinkEdge?.link || !mplsLinkEdge?.node || !mplsLinkEdge.node.mplsDevice) { + return null; + } + + return { + id: mplsLinkEdge.link, + source: { + interface: i.id, + nodeId: i.nodeId, + }, + target: { + interface: mplsLinkEdge.node.id, + nodeId: mplsLinkEdge.node.mplsDevice?.name, + }, + }; + }) + .filter(omitNullValue) ?? []; + + return links; + }); +} diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 3c7331a2..ea194f33 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -493,6 +493,49 @@ type LocationEdge { node: Location! } +type LspTunnel { + fromDevice: String + lspId: String! + signalization: Signalization + toDevice: String + uptime: Int +} + +type MplsData { + inputInterface: String + inputLabel: Int + lspId: String! + outputInterface: String + outputLabel: Int +} + +type MplsDeviceDetails { + lspTunnels: [LspTunnel] + mplsData: [MplsData] +} + +type MplsGraphNode { + coordinates: GraphNodeCoordinates! + id: ID! + interfaces: [MplsGraphNodeInterface!]! + labels: [String!] + mplsDeviceDetails: MplsDeviceDetails! + name: String! + nodeId: String! + status: GraphEdgeStatus! +} + +type MplsGraphNodeInterface { + id: String! + name: String! + status: GraphEdgeStatus! +} + +type MplsTopology { + edges: [GraphEdge!]! + nodes: [MplsGraphNode!]! +} + type Mutation { activateStream(id: String!): ActivateStreamPayload! addBlueprint(input: AddBlueprintInput!): AddBlueprintPayload! @@ -665,6 +708,7 @@ type Query { kafkaHealthCheck: IsOkResponse labels(after: String, before: String, filter: FilterLabelsInput, first: Int, last: Int): LabelConnection! locations(after: String, before: String, first: Int, last: Int): LocationConnection! + mplsTopology: MplsTopology netTopology: NetTopology netTopologyVersionData(version: String!): NetTopologyVersionData! node(id: ID!): Node @@ -701,6 +745,11 @@ type RevertChangesPayload { isOk: Boolean! } +enum Signalization { + LDP + RSVP +} + type Snapshot { createdAt: String! name: String! @@ -814,6 +863,7 @@ type TopologyCommonNodes { enum TopologyLayer { EthTopology + MplsTopology PhysicalTopology PtpTopology } diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index e50be399..b00f8250 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -207,10 +207,11 @@ export interface NexusGenEnums { DeviceSize: 'LARGE' | 'MEDIUM' | 'SMALL'; DeviceSource: 'DISCOVERED' | 'IMPORTED' | 'MANUAL'; GraphEdgeStatus: 'ok' | 'unknown'; + Signalization: 'LDP' | 'RSVP'; SortDeviceBy: 'discoveredAt' | 'modelVersion' | 'name'; SortDirection: 'ASC' | 'DESC'; SortStreamBy: 'createdAt' | 'deviceName' | 'streamName'; - TopologyLayer: 'EthTopology' | 'PhysicalTopology' | 'PtpTopology'; + TopologyLayer: 'EthTopology' | 'MplsTopology' | 'PhysicalTopology' | 'PtpTopology'; } export interface NexusGenScalars { @@ -504,6 +505,49 @@ export interface NexusGenObjects { cursor: string; // String! node: NexusGenRootTypes['Location']; // Location! }; + LspTunnel: { + // root type + fromDevice?: string | null; // String + lspId: string; // String! + signalization?: NexusGenEnums['Signalization'] | null; // Signalization + toDevice?: string | null; // String + uptime?: number | null; // Int + }; + MplsData: { + // root type + inputInterface?: string | null; // String + inputLabel?: number | null; // Int + lspId: string; // String! + outputInterface?: string | null; // String + outputLabel?: number | null; // Int + }; + MplsDeviceDetails: { + // root type + lspTunnels?: Array | null; // [LspTunnel] + mplsData?: Array | null; // [MplsData] + }; + MplsGraphNode: { + // root type + coordinates: NexusGenRootTypes['GraphNodeCoordinates']; // GraphNodeCoordinates! + id: string; // ID! + interfaces: NexusGenRootTypes['MplsGraphNodeInterface'][]; // [MplsGraphNodeInterface!]! + labels?: string[] | null; // [String!] + mplsDeviceDetails: NexusGenRootTypes['MplsDeviceDetails']; // MplsDeviceDetails! + name: string; // String! + nodeId: string; // String! + status: NexusGenEnums['GraphEdgeStatus']; // GraphEdgeStatus! + }; + MplsGraphNodeInterface: { + // root type + id: string; // String! + name: string; // String! + status: NexusGenEnums['GraphEdgeStatus']; // GraphEdgeStatus! + }; + MplsTopology: { + // root type + edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! + nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! + }; Mutation: {}; NetInterface: { // root type @@ -1111,6 +1155,49 @@ export interface NexusGenFieldTypes { cursor: string; // String! node: NexusGenRootTypes['Location']; // Location! }; + LspTunnel: { + // field return type + fromDevice: string | null; // String + lspId: string; // String! + signalization: NexusGenEnums['Signalization'] | null; // Signalization + toDevice: string | null; // String + uptime: number | null; // Int + }; + MplsData: { + // field return type + inputInterface: string | null; // String + inputLabel: number | null; // Int + lspId: string; // String! + outputInterface: string | null; // String + outputLabel: number | null; // Int + }; + MplsDeviceDetails: { + // field return type + lspTunnels: Array | null; // [LspTunnel] + mplsData: Array | null; // [MplsData] + }; + MplsGraphNode: { + // field return type + coordinates: NexusGenRootTypes['GraphNodeCoordinates']; // GraphNodeCoordinates! + id: string; // ID! + interfaces: NexusGenRootTypes['MplsGraphNodeInterface'][]; // [MplsGraphNodeInterface!]! + labels: string[] | null; // [String!] + mplsDeviceDetails: NexusGenRootTypes['MplsDeviceDetails']; // MplsDeviceDetails! + name: string; // String! + nodeId: string; // String! + status: NexusGenEnums['GraphEdgeStatus']; // GraphEdgeStatus! + }; + MplsGraphNodeInterface: { + // field return type + id: string; // String! + name: string; // String! + status: NexusGenEnums['GraphEdgeStatus']; // GraphEdgeStatus! + }; + MplsTopology: { + // field return type + edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! + nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! + }; Mutation: { // field return type activateStream: NexusGenRootTypes['ActivateStreamPayload']; // ActivateStreamPayload! @@ -1273,6 +1360,7 @@ export interface NexusGenFieldTypes { kafkaHealthCheck: NexusGenRootTypes['IsOkResponse'] | null; // IsOkResponse labels: NexusGenRootTypes['LabelConnection']; // LabelConnection! locations: NexusGenRootTypes['LocationConnection']; // LocationConnection! + mplsTopology: NexusGenRootTypes['MplsTopology'] | null; // MplsTopology netTopology: NexusGenRootTypes['NetTopology'] | null; // NetTopology netTopologyVersionData: NexusGenRootTypes['NetTopologyVersionData']; // NetTopologyVersionData! node: NexusGenRootTypes['Node'] | null; // Node @@ -1803,6 +1891,49 @@ export interface NexusGenFieldTypeNames { cursor: 'String'; node: 'Location'; }; + LspTunnel: { + // field return type name + fromDevice: 'String'; + lspId: 'String'; + signalization: 'Signalization'; + toDevice: 'String'; + uptime: 'Int'; + }; + MplsData: { + // field return type name + inputInterface: 'String'; + inputLabel: 'Int'; + lspId: 'String'; + outputInterface: 'String'; + outputLabel: 'Int'; + }; + MplsDeviceDetails: { + // field return type name + lspTunnels: 'LspTunnel'; + mplsData: 'MplsData'; + }; + MplsGraphNode: { + // field return type name + coordinates: 'GraphNodeCoordinates'; + id: 'ID'; + interfaces: 'MplsGraphNodeInterface'; + labels: 'String'; + mplsDeviceDetails: 'MplsDeviceDetails'; + name: 'String'; + nodeId: 'String'; + status: 'GraphEdgeStatus'; + }; + MplsGraphNodeInterface: { + // field return type name + id: 'String'; + name: 'String'; + status: 'GraphEdgeStatus'; + }; + MplsTopology: { + // field return type name + edges: 'GraphEdge'; + nodes: 'MplsGraphNode'; + }; Mutation: { // field return type name activateStream: 'ActivateStreamPayload'; @@ -1965,6 +2096,7 @@ export interface NexusGenFieldTypeNames { kafkaHealthCheck: 'IsOkResponse'; labels: 'LabelConnection'; locations: 'LocationConnection'; + mplsTopology: 'MplsTopology'; netTopology: 'NetTopology'; netTopologyVersionData: 'NetTopologyVersionData'; node: 'Node'; diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 9ac98118..18965c0b 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -31,6 +31,8 @@ import { getSynceNodesFromTopologyQuery, getSynceTopologyInterfaces, getTopologyInterfaces, + makeMplsTopologyEdges, + makeMplsTopologyNodes, makeNetTopologyDiff, makeNetTopologyEdges, makeNetTopologyNodes, @@ -249,6 +251,64 @@ export const SynceGraphNode = objectType({ }, }); +export const MplsData = objectType({ + name: 'MplsData', + definition: (t) => { + t.nonNull.string('lspId'); + t.int('inputLabel'); + t.string('inputInterface'); + t.int('outputLabel'); + t.string('outputInterface'); + }, +}); + +export const Signalization = enumType({ + name: 'Signalization', + members: ['RSVP', 'LDP'], +}); + +export const LspTunnel = objectType({ + name: 'LspTunnel', + definition: (t) => { + t.nonNull.string('lspId'); + t.string('fromDevice'); + t.string('toDevice'); + t.int('uptime'); + t.field('signalization', { type: Signalization }); + }, +}); + +export const MplsDeviceDetails = objectType({ + name: 'MplsDeviceDetails', + definition: (t) => { + t.list.field('mplsData', { type: MplsData }); + t.list.field('lspTunnels', { type: LspTunnel }); + }, +}); + +export const MplsGraphNodeInterface = objectType({ + name: 'MplsGraphNodeInterface', + definition: (t) => { + t.nonNull.string('id'); + t.nonNull.field('status', { type: GraphInterfaceStatus }); + t.nonNull.string('name'); + }, +}); + +export const MplsGraphNode = objectType({ + name: 'MplsGraphNode', + definition: (t) => { + t.nonNull.id('id'); + t.nonNull.string('nodeId'); + t.nonNull.string('name'); + t.nonNull.field('mplsDeviceDetails', { type: MplsDeviceDetails }); + t.nonNull.field('status', { type: GraphInterfaceStatus }); + t.list.nonNull.string('labels'); + t.nonNull.list.nonNull.field('interfaces', { type: nonNull(MplsGraphNodeInterface) }); + t.nonNull.field('coordinates', { type: GraphNodeCoordinates }); + }, +}); + export const PhyTopologyVersionData = objectType({ name: 'PhyTopologyVersionData', definition: (t) => { @@ -518,7 +578,7 @@ export const GraphNodeCoordinatesInput = inputObjectType({ export const TopologyLayer = enumType({ name: 'TopologyLayer', - members: ['PhysicalTopology', 'PtpTopology', 'EthTopology'], + members: ['PhysicalTopology', 'PtpTopology', 'EthTopology', 'MplsTopology'], }); export const UpdateGraphNodeCooordinatesInput = inputObjectType({ @@ -814,3 +874,26 @@ export const deviceMetadataQuery = queryField('deviceMetadata', { : null; }, }); + +export const MplsTopology = objectType({ + name: 'MplsTopology', + definition: (t) => { + t.nonNull.list.field('edges', { type: nonNull(GraphEdge) }); + t.nonNull.list.field('nodes', { type: nonNull(MplsGraphNode) }); + }, +}); + +export const MplsTopologyQuery = queryField('mplsTopology', { + type: 'MplsTopology', + resolve: async (_, _args, { topologyDiscoveryGraphQLAPI }) => { + const mplsTopologyResult = await topologyDiscoveryGraphQLAPI?.getMplsTopology(); + + const nodes = makeMplsTopologyNodes(mplsTopologyResult); + const edges = makeMplsTopologyEdges(mplsTopologyResult); + + return { + nodes, + edges, + }; + }, +}); From de1b51153874afbe68a82fb0f6388d44e2227b31 Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Wed, 7 Aug 2024 15:22:15 +0200 Subject: [PATCH 07/18] updateLocation, deleteLocation mutations added (#460) * updateLocation mutation added * deleteLocation mutation added --- src/schema/api.graphql | 16 +++++++ src/schema/location.ts | 83 ++++++++++++++++++++++++++++++++++++- src/schema/nexus-typegen.ts | 43 +++++++++++++++++++ 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/src/schema/api.graphql b/src/schema/api.graphql index ea194f33..f72c0fe4 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -243,6 +243,10 @@ type DeleteLabelPayload { label: Label } +type DeleteLocationPayload { + location: Location! +} + input DeleteSnapshotInput { deviceId: String! name: String! @@ -557,6 +561,7 @@ type Mutation { deleteBlueprint(id: String!): DeleteBlueprintPayload! deleteDevice(id: String!): DeleteDevicePayload! deleteLabel(id: String!): DeleteLabelPayload! + deleteLocation(id: String!): DeleteLocationPayload! deleteSnapshot(input: DeleteSnapshotInput!): DeleteSnapshotPayload deleteStream(id: String!): DeleteStreamPayload! importCSV(input: CSVImportInput!): CSVImport @@ -571,6 +576,7 @@ type Mutation { updateDevice(id: String!, input: UpdateDeviceInput!): UpdateDevicePayload! updateDiscoveredAt(deviceIds: [String!]!): [DeviceDiscoveryPayload!]! updateGraphNodeCoordinates(input: UpdateGraphNodeCoordinatesInput!): UpdateGraphNodeCoordinatesPayload! + updateLocation(id: String!, input: UpdateLocationInput!): UpdateLocationPayload! updateStream(id: String!, input: UpdateStreamInput!): UpdateStreamPayload! } @@ -940,6 +946,16 @@ type UpdateGraphNodeCoordinatesPayload { deviceNames: [String!]! } +input UpdateLocationInput { + coordinates: Coordinates! + countryId: String + name: String! +} + +type UpdateLocationPayload { + location: Location! +} + input UpdateStreamInput { blueprintId: String deviceName: String! diff --git a/src/schema/location.ts b/src/schema/location.ts index de19b0ec..818849ea 100644 --- a/src/schema/location.ts +++ b/src/schema/location.ts @@ -1,8 +1,8 @@ import { findManyCursorConnection } from '@devoxa/prisma-relay-cursor-connection'; import { connectionFromArray } from 'graphql-relay'; import countries from 'i18n-iso-countries'; -import { arg, extendType, inputObjectType, nonNull, objectType } from 'nexus'; -import { toGraphId } from '../helpers/id-helper'; +import { arg, extendType, inputObjectType, nonNull, objectType, stringArg } from 'nexus'; +import { fromGraphId, toGraphId } from '../helpers/id-helper'; import { Node, PageInfo, PaginationConnectionArgs } from './global-types'; import { getCountryName } from '../helpers/location-helpers'; @@ -165,3 +165,82 @@ export const AddLocationMutation = extendType({ }); }, }); + +export const UpdateLocationInput = inputObjectType({ + name: 'UpdateLocationInput', + definition: (t) => { + t.nonNull.string('name'); + t.string('countryId'); + t.nonNull.field({ name: 'coordinates', type: Coordinates }); + }, +}); + +export const UpdateLocationPayload = objectType({ + name: 'UpdateLocationPayload', + definition: (t) => { + t.nonNull.field('location', { type: Location }); + }, +}); + +export const UpdateLocationMutation = extendType({ + type: 'Mutation', + definition: (t) => { + t.nonNull.field('updateLocation', { + type: UpdateLocationPayload, + args: { + id: nonNull(stringArg()), + input: nonNull(arg({ type: UpdateLocationInput })), + }, + resolve: async (_, args, { prisma, tenantId }) => { + const nativeId = fromGraphId('Location', args.id); + const { input } = args; + + const countryName = getCountryName(input.countryId ?? null); + + const location = await prisma.location.update({ + where: { id: nativeId }, + data: { + tenantId, + name: input.name, + country: countryName, + latitude: input.coordinates.latitude.toString(), + longitude: input.coordinates.latitude.toString(), + }, + }); + return { + location, + }; + }, + }); + }, +}); + +export const DeleteLocationPayload = objectType({ + name: 'DeleteLocationPayload', + definition: (t) => { + t.nonNull.field('location', { type: Location }); + }, +}); + +export const DeleteLocationMutation = extendType({ + type: 'Mutation', + definition: (t) => { + t.nonNull.field('deleteLocation', { + type: DeleteLocationPayload, + args: { + id: nonNull(stringArg()), + }, + resolve: async (_, args, { prisma, tenantId }) => { + const nativeId = fromGraphId('Location', args.id); + + const location = await prisma.location.delete({ + where: { id: nativeId, AND: { tenantId } }, + }); + + return { + location, + }; + }, + }); + }, +}); diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index b00f8250..f8d95852 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -193,6 +193,12 @@ export interface NexusGenInputs { coordinates: NexusGenInputs['GraphNodeCoordinatesInput'][]; // [GraphNodeCoordinatesInput!]! layer?: NexusGenEnums['TopologyLayer'] | null; // TopologyLayer }; + UpdateLocationInput: { + // input type + coordinates: NexusGenInputs['Coordinates']; // Coordinates! + countryId?: string | null; // String + name: string; // String! + }; UpdateStreamInput: { // input type blueprintId?: string | null; // String @@ -355,6 +361,10 @@ export interface NexusGenObjects { // root type label?: NexusGenRootTypes['Label'] | null; // Label }; + DeleteLocationPayload: { + // root type + location: NexusGenRootTypes['Location']; // Location! + }; DeleteSnapshotPayload: { // root type snapshot?: NexusGenRootTypes['Snapshot'] | null; // Snapshot @@ -783,6 +793,10 @@ export interface NexusGenObjects { // root type deviceNames: string[]; // [String!]! }; + UpdateLocationPayload: { + // root type + location: NexusGenRootTypes['Location']; // Location! + }; UpdateStreamPayload: { // root type stream?: NexusGenRootTypes['Stream'] | null; // Stream @@ -968,6 +982,10 @@ export interface NexusGenFieldTypes { // field return type label: NexusGenRootTypes['Label'] | null; // Label }; + DeleteLocationPayload: { + // field return type + location: NexusGenRootTypes['Location']; // Location! + }; DeleteSnapshotPayload: { // field return type snapshot: NexusGenRootTypes['Snapshot'] | null; // Snapshot @@ -1220,6 +1238,7 @@ export interface NexusGenFieldTypes { deleteBlueprint: NexusGenRootTypes['DeleteBlueprintPayload']; // DeleteBlueprintPayload! deleteDevice: NexusGenRootTypes['DeleteDevicePayload']; // DeleteDevicePayload! deleteLabel: NexusGenRootTypes['DeleteLabelPayload']; // DeleteLabelPayload! + deleteLocation: NexusGenRootTypes['DeleteLocationPayload']; // DeleteLocationPayload! deleteSnapshot: NexusGenRootTypes['DeleteSnapshotPayload'] | null; // DeleteSnapshotPayload deleteStream: NexusGenRootTypes['DeleteStreamPayload']; // DeleteStreamPayload! importCSV: NexusGenRootTypes['CSVImport'] | null; // CSVImport @@ -1234,6 +1253,7 @@ export interface NexusGenFieldTypes { updateDevice: NexusGenRootTypes['UpdateDevicePayload']; // UpdateDevicePayload! updateDiscoveredAt: NexusGenRootTypes['DeviceDiscoveryPayload'][]; // [DeviceDiscoveryPayload!]! updateGraphNodeCoordinates: NexusGenRootTypes['UpdateGraphNodeCoordinatesPayload']; // UpdateGraphNodeCoordinatesPayload! + updateLocation: NexusGenRootTypes['UpdateLocationPayload']; // UpdateLocationPayload! updateStream: NexusGenRootTypes['UpdateStreamPayload']; // UpdateStreamPayload! }; NetInterface: { @@ -1519,6 +1539,10 @@ export interface NexusGenFieldTypes { // field return type deviceNames: string[]; // [String!]! }; + UpdateLocationPayload: { + // field return type + location: NexusGenRootTypes['Location']; // Location! + }; UpdateStreamPayload: { // field return type stream: NexusGenRootTypes['Stream'] | null; // Stream @@ -1704,6 +1728,10 @@ export interface NexusGenFieldTypeNames { // field return type name label: 'Label'; }; + DeleteLocationPayload: { + // field return type name + location: 'Location'; + }; DeleteSnapshotPayload: { // field return type name snapshot: 'Snapshot'; @@ -1956,6 +1984,7 @@ export interface NexusGenFieldTypeNames { deleteBlueprint: 'DeleteBlueprintPayload'; deleteDevice: 'DeleteDevicePayload'; deleteLabel: 'DeleteLabelPayload'; + deleteLocation: 'DeleteLocationPayload'; deleteSnapshot: 'DeleteSnapshotPayload'; deleteStream: 'DeleteStreamPayload'; importCSV: 'CSVImport'; @@ -1970,6 +1999,7 @@ export interface NexusGenFieldTypeNames { updateDevice: 'UpdateDevicePayload'; updateDiscoveredAt: 'DeviceDiscoveryPayload'; updateGraphNodeCoordinates: 'UpdateGraphNodeCoordinatesPayload'; + updateLocation: 'UpdateLocationPayload'; updateStream: 'UpdateStreamPayload'; }; NetInterface: { @@ -2255,6 +2285,10 @@ export interface NexusGenFieldTypeNames { // field return type name deviceNames: 'String'; }; + UpdateLocationPayload: { + // field return type name + location: 'Location'; + }; UpdateStreamPayload: { // field return type name stream: 'Stream'; @@ -2386,6 +2420,10 @@ export interface NexusGenArgTypes { // args id: string; // String! }; + deleteLocation: { + // args + id: string; // String! + }; deleteSnapshot: { // args input: NexusGenInputs['DeleteSnapshotInput']; // DeleteSnapshotInput! @@ -2444,6 +2482,11 @@ export interface NexusGenArgTypes { // args input: NexusGenInputs['UpdateGraphNodeCoordinatesInput']; // UpdateGraphNodeCoordinatesInput! }; + updateLocation: { + // args + id: string; // String! + input: NexusGenInputs['UpdateLocationInput']; // UpdateLocationInput! + }; updateStream: { // args id: string; // String! From bce494182bfa423b1edf8acfec273685bd212bc1 Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Mon, 12 Aug 2024 13:08:05 +0200 Subject: [PATCH 08/18] mpls changes (#461) * mpls changes * updateLocation fix --- .../topology-discovery.graphql.ts | 35 +++++++++++++++---- .../topology-discovery-graphql.ts | 12 ++++--- src/helpers/topology.helpers.ts | 11 +++--- src/schema/api.graphql | 3 ++ src/schema/location.ts | 4 +-- src/schema/nexus-typegen.ts | 9 +++++ src/schema/topology.ts | 3 ++ 7 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index 3a73e09c..8577eb51 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -158,15 +158,23 @@ export type MetadataConnection = { export type MplsData = { __typename?: 'MplsData'; /** The input interface. */ - input_interface: Maybe; + in_interface: Maybe; /** The input label. */ - input_label: Maybe; + in_label: Maybe; + /** Where the tunnel is headed (Router IP.) */ + ldp_prefix: Maybe; /** Name of the link state packet. */ lsp_id: Scalars['String']; + /** The operation type. */ + mpls_operation: Maybe; + /** Operational state of the device. */ + oper_state: Maybe; /** The input interface. */ - output_interface: Maybe; + out_interface: Maybe; /** The output label. */ - output_label: Maybe; + out_label: Maybe; + /** Type of signalisation. */ + signalisation: Maybe; }; /** Representation of the device in the MPLS topology. */ @@ -298,6 +306,12 @@ export type MplsLinkEdge = { node: Maybe; }; +export type MplsOperation = + | 'Noop' + | 'Pop' + | 'Push' + | 'Swap'; + export type MplsTotalLsps = { __typename?: 'MplsTotalLsps'; /** Number of incoming LSPs. */ @@ -662,6 +676,8 @@ export type PhyDeviceFilter = { /** Port attached to the physical device. */ export type PhyInterface = Node & { __typename?: 'PhyInterface'; + /** Details of the interface. */ + details: Maybe; /** Unique identifier of the object. */ id: Scalars['ID']; /** Human readable name of the network port. */ @@ -690,6 +706,13 @@ export type PhyInterfaceConnection = { pageInfo: PageInfo; }; +/** Details of the interface. */ +export type PhyInterfaceDetails = { + __typename?: 'PhyInterfaceDetails'; + /** Max operational interface bandwidth in Mbit. */ + max_speed: Maybe; +}; + /** Grouped PhyInterface object and associated cursor used by pagination. */ export type PhyInterfaceEdge = { __typename?: 'PhyInterfaceEdge'; @@ -1509,7 +1532,7 @@ export type DeviceMetadataQueryVariables = Exact<{ export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; -export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, input_label: number | null, input_interface: string | null, output_interface: string | null, output_label: number | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; +export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; export type MplsInterfaceDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } }; @@ -1518,4 +1541,4 @@ export type MplsInterfacePartsFragment = { __typename?: 'MplsInterface', id: str export type MplsTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, input_label: number | null, input_interface: string | null, output_interface: string | null, output_label: number | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; +export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 4b647ff2..cfd24f83 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -454,10 +454,14 @@ const MPLS_TOPOLOGY = gql` details { mpls_data { lsp_id - input_label - input_interface - output_interface - output_label + in_label + in_interface + out_interface + out_label + ldp_prefix + mpls_operation + oper_state + signalisation } lsp_tunnels { lsp_id diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index ffd13258..4a8eac83 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -1629,10 +1629,13 @@ export function makeMplsTopologyNodes(mplsDevices?: MplsTopologyQuery) { mplsData: node.details.mpls_data?.map((d) => ({ lspId: d?.lsp_id ?? '', - inputLabel: d?.input_label ?? null, - inputInterface: d?.input_interface ?? null, - outputInterface: d?.output_interface ?? null, - outputLabel: d?.output_label ?? null, + inputLabel: d?.in_label ?? null, + inputInterface: d?.in_interface ?? null, + outputInterface: d?.out_interface ?? null, + outputLabel: d?.out_label ?? null, + operState: d?.oper_state ?? null, + ldpPrefix: d?.ldp_prefix ?? null, + mplsOperation: d?.mpls_operation ?? null, })) ?? null, }, }; diff --git a/src/schema/api.graphql b/src/schema/api.graphql index f72c0fe4..f828ad21 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -508,7 +508,10 @@ type LspTunnel { type MplsData { inputInterface: String inputLabel: Int + ldpPrefix: String lspId: String! + mplsOperation: String + operState: String outputInterface: String outputLabel: Int } diff --git a/src/schema/location.ts b/src/schema/location.ts index 818849ea..5498075e 100644 --- a/src/schema/location.ts +++ b/src/schema/location.ts @@ -155,7 +155,7 @@ export const AddLocationMutation = extendType({ name: input.name, country: countryName, latitude: input.coordinates.latitude.toString(), - longitude: input.coordinates.latitude.toString(), + longitude: input.coordinates.longitude.toString(), }, }); return { @@ -204,7 +204,7 @@ export const UpdateLocationMutation = extendType({ name: input.name, country: countryName, latitude: input.coordinates.latitude.toString(), - longitude: input.coordinates.latitude.toString(), + longitude: input.coordinates.longitude.toString(), }, }); return { diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index f8d95852..b6af3978 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -527,7 +527,10 @@ export interface NexusGenObjects { // root type inputInterface?: string | null; // String inputLabel?: number | null; // Int + ldpPrefix?: string | null; // String lspId: string; // String! + mplsOperation?: string | null; // String + operState?: string | null; // String outputInterface?: string | null; // String outputLabel?: number | null; // Int }; @@ -1185,7 +1188,10 @@ export interface NexusGenFieldTypes { // field return type inputInterface: string | null; // String inputLabel: number | null; // Int + ldpPrefix: string | null; // String lspId: string; // String! + mplsOperation: string | null; // String + operState: string | null; // String outputInterface: string | null; // String outputLabel: number | null; // Int }; @@ -1931,7 +1937,10 @@ export interface NexusGenFieldTypeNames { // field return type name inputInterface: 'String'; inputLabel: 'Int'; + ldpPrefix: 'String'; lspId: 'String'; + mplsOperation: 'String'; + operState: 'String'; outputInterface: 'String'; outputLabel: 'Int'; }; diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 18965c0b..ad49a785 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -259,6 +259,9 @@ export const MplsData = objectType({ t.string('inputInterface'); t.int('outputLabel'); t.string('outputInterface'); + t.string('operState'); + t.string('ldpPrefix'); + t.string('mplsOperation'); }, }); From 8477694b135ab925b8e78e6fb821f36b3ed873f6 Mon Sep 17 00:00:00 2001 From: plehocky <117287338+plehocky@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:33:12 +0200 Subject: [PATCH 09/18] Fr 327 show overlay topology (#462) * added filtering arguments to the DeviceMetaData query * formatting * adjusted filter parameters --- .../topology-discovery-graphql.ts | 10 ++++-- src/schema/api.graphql | 20 +++++++++++- src/schema/nexus-typegen.ts | 15 +++++++++ src/schema/topology.ts | 32 ++++++++++++++++++- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index cfd24f83..26ec42e3 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -33,6 +33,12 @@ type CoordinatesParam = { y: number; }; +type DeviceMetadataFilters = { + deviceName?: string | null; + topologyType?: 'PhysicalTopology' | 'PtpTopology' | 'EthTopology' | 'NetworkTopology' | 'MplsTopology' | null; + polygon?: number[][][] | null; +}; + const GET_SHORTEST_PATH = gql` query GetShortestPath($deviceFrom: ID!, $deviceTo: ID!, $collection: NetRoutingPathOutputCollections) { netRoutingPaths(deviceFrom: $deviceFrom, deviceTo: $deviceTo, outputCollection: $collection) { @@ -644,9 +650,9 @@ function getTopologyDiscoveryApi() { return response.syncePathToGm.nodes; } - async function getDeviceMetadata(): Promise { + async function getDeviceMetadata(filters?: DeviceMetadataFilters): Promise { const response = await client.request(DEVICE_METADATA, { - filters: undefined, + filters, }); return response; } diff --git a/src/schema/api.graphql b/src/schema/api.graphql index f828ad21..4091f9a9 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -366,6 +366,12 @@ input FilterDevicesInput { labels: [String!] } +input FilterDevicesMetadatasInput { + deviceName: String + polygon: PolygonInput + topologyType: TopologyType +} + input FilterLabelsInput { name: String! } @@ -639,6 +645,10 @@ type PhyTopologyVersionData { nodes: [GraphVersionNode!]! } +input PolygonInput { + polygon: [[[Float!]!]!] +} + type PtpDeviceDetails { clockAccuracy: String clockClass: Int @@ -705,7 +715,7 @@ type Query { calculatedDiff(deviceId: String!, transactionId: String!): CalculatedDiffPayload! countries(after: String, before: String, first: Int, last: Int): CountryConnection! dataStore(deviceId: String!, transactionId: String!): DataStore - deviceMetadata: DeviceMetadata + deviceMetadata(filter: FilterDevicesMetadatasInput): DeviceMetadata devices( after: String before: String @@ -877,6 +887,14 @@ enum TopologyLayer { PtpTopology } +enum TopologyType { + EthTopology + MplsTopology + NetworkTopology + PhysicalTopology + PtpTopology +} + type Transaction { changes: [TransactionChange!]! lastCommitTime: String! diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index b6af3978..c65deaa4 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -133,6 +133,12 @@ export interface NexusGenInputs { deviceName?: string | null; // String labels?: string[] | null; // [String!] }; + FilterDevicesMetadatasInput: { + // input type + deviceName?: string | null; // String + polygon?: NexusGenInputs['PolygonInput'] | null; // PolygonInput + topologyType?: NexusGenEnums['TopologyType'] | null; // TopologyType + }; FilterLabelsInput: { // input type name: string; // String! @@ -157,6 +163,10 @@ export interface NexusGenInputs { x: number; // Float! y: number; // Float! }; + PolygonInput: { + // input type + polygon?: number[][][] | null; // [[[Float!]!]!] + }; StreamOrderByInput: { // input type direction: NexusGenEnums['SortDirection']; // SortDirection! @@ -218,6 +228,7 @@ export interface NexusGenEnums { SortDirection: 'ASC' | 'DESC'; SortStreamBy: 'createdAt' | 'deviceName' | 'streamName'; TopologyLayer: 'EthTopology' | 'MplsTopology' | 'PhysicalTopology' | 'PtpTopology'; + TopologyType: 'EthTopology' | 'MplsTopology' | 'NetworkTopology' | 'PhysicalTopology' | 'PtpTopology'; } export interface NexusGenScalars { @@ -2527,6 +2538,10 @@ export interface NexusGenArgTypes { deviceId: string; // String! transactionId: string; // String! }; + deviceMetadata: { + // args + filter?: NexusGenInputs['FilterDevicesMetadatasInput'] | null; // FilterDevicesMetadatasInput + }; devices: { // args after?: string | null; // String diff --git a/src/schema/topology.ts b/src/schema/topology.ts index ad49a785..8fd13b21 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -8,6 +8,7 @@ import { enumType, interfaceType, queryField, + arg, } from 'nexus'; import config from '../config'; import { fromGraphId } from '../helpers/id-helper'; @@ -853,13 +854,42 @@ export const DeviceMetadata = objectType({ }, }); +export const TopologyType = enumType({ + name: 'TopologyType', + members: ['PhysicalTopology', 'PtpTopology', 'EthTopology', 'NetworkTopology', 'MplsTopology'], +}); + +export const PolygonInputType = inputObjectType({ + name: 'PolygonInput', + definition(t) { + t.list.nonNull.list.nonNull.list.nonNull.float('polygon'); + }, +}); + +export const FilterDevicesMetadatasInput = inputObjectType({ + name: 'FilterDevicesMetadatasInput', + definition: (t) => { + t.string('deviceName'); + t.field('topologyType', { type: TopologyType }); + t.field('polygon', { type: PolygonInputType }); + }, +}); + export const deviceMetadataQuery = queryField('deviceMetadata', { type: DeviceMetadata, + args: { + filter: FilterDevicesMetadatasInput, + }, resolve: async (_, args, { prisma, topologyDiscoveryGraphQLAPI }) => { if (!topologyDiscoveryGraphQLAPI) { return null; } - const deviceMetadataResult = await topologyDiscoveryGraphQLAPI.getDeviceMetadata(); + + const deviceMetadataResult = await topologyDiscoveryGraphQLAPI.getDeviceMetadata({ + deviceName: args.filter?.deviceName, + topologyType: args.filter?.topologyType, + polygon: args.filter?.polygon?.polygon, + }); const dbDevices = await prisma.device.findMany({ include: { From e5b14ea4034231ec3290da835489e80a47356106 Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Mon, 19 Aug 2024 11:52:22 +0200 Subject: [PATCH 10/18] mpls count queries added (#463) --- .../topology-discovery.graphql.ts | 7 ++++ .../topology-discovery-graphql.ts | 21 ++++++++++ src/schema/api.graphql | 11 +++++ src/schema/nexus-typegen.ts | 36 +++++++++++++++++ src/schema/topology.ts | 40 +++++++++++++++++++ 5 files changed, 115 insertions(+) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index 8577eb51..ac1e4008 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -1542,3 +1542,10 @@ export type MplsTopologyQueryVariables = Exact<{ [key: string]: never; }>; export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; + +export type MplsLspCountQueryVariables = Exact<{ + deviceId: Scalars['ID']; +}>; + + +export type MplsLspCountQuery = { __typename?: 'Query', mplsLspCount: Array<{ __typename?: 'MplsTotalLsps', to_device: string | null, incoming_lsps: number | null, outcoming_lsps: number | null } | null> | null }; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 26ec42e3..754f9e49 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -24,6 +24,8 @@ import { DeviceMetadataQuery, DeviceMetadataQueryVariables, MplsTopologyQuery, + MplsLspCountQuery, + MplsLspCountQueryVariables, } from '../__generated__/topology-discovery.graphql'; import { TopologyDiffOutput, decodeTopologyDiffOutput } from './topology-network-types'; @@ -537,6 +539,16 @@ const MPLS_TOPOLOGY = gql` } `; +const MPLS_LSP_COUNT = gql` + query MplsLspCount($deviceId: ID!) { + mplsLspCount(device_id: $deviceId) { + to_device + incoming_lsps + outcoming_lsps + } + } +`; + function getTopologyDiscoveryApi() { if (!config.topologyEnabled) { return undefined; @@ -663,6 +675,14 @@ function getTopologyDiscoveryApi() { return response; } + async function getMplsLspCount(deviceId: string): Promise { + const response = await client.request(MPLS_LSP_COUNT, { + deviceId, + }); + + return response; + } + return { getTopologyDevices, getPtpDiffSynce, @@ -678,6 +698,7 @@ function getTopologyDiscoveryApi() { getSyncePathToGrandMaster, getDeviceMetadata, getMplsTopology, + getMplsLspCount, }; } diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 4091f9a9..6fde991e 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -544,6 +544,16 @@ type MplsGraphNodeInterface { status: GraphEdgeStatus! } +type MplsLspCount { + counts: [MplsLspCountItem] +} + +type MplsLspCountItem { + incomingLsps: Int + outcomingLsps: Int + target: String +} + type MplsTopology { edges: [GraphEdge!]! nodes: [MplsGraphNode!]! @@ -727,6 +737,7 @@ type Query { kafkaHealthCheck: IsOkResponse labels(after: String, before: String, filter: FilterLabelsInput, first: Int, last: Int): LabelConnection! locations(after: String, before: String, first: Int, last: Int): LocationConnection! + mplsLspCount(deviceId: String!): MplsLspCount mplsTopology: MplsTopology netTopology: NetTopology netTopologyVersionData(version: String!): NetTopologyVersionData! diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index c65deaa4..04fdbd3f 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -567,6 +567,16 @@ export interface NexusGenObjects { name: string; // String! status: NexusGenEnums['GraphEdgeStatus']; // GraphEdgeStatus! }; + MplsLspCount: { + // root type + counts?: Array | null; // [MplsLspCountItem] + }; + MplsLspCountItem: { + // root type + incomingLsps?: number | null; // Int + outcomingLsps?: number | null; // Int + target?: string | null; // String + }; MplsTopology: { // root type edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! @@ -1228,6 +1238,16 @@ export interface NexusGenFieldTypes { name: string; // String! status: NexusGenEnums['GraphEdgeStatus']; // GraphEdgeStatus! }; + MplsLspCount: { + // field return type + counts: Array | null; // [MplsLspCountItem] + }; + MplsLspCountItem: { + // field return type + incomingLsps: number | null; // Int + outcomingLsps: number | null; // Int + target: string | null; // String + }; MplsTopology: { // field return type edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! @@ -1397,6 +1417,7 @@ export interface NexusGenFieldTypes { kafkaHealthCheck: NexusGenRootTypes['IsOkResponse'] | null; // IsOkResponse labels: NexusGenRootTypes['LabelConnection']; // LabelConnection! locations: NexusGenRootTypes['LocationConnection']; // LocationConnection! + mplsLspCount: NexusGenRootTypes['MplsLspCount'] | null; // MplsLspCount mplsTopology: NexusGenRootTypes['MplsTopology'] | null; // MplsTopology netTopology: NexusGenRootTypes['NetTopology'] | null; // NetTopology netTopologyVersionData: NexusGenRootTypes['NetTopologyVersionData']; // NetTopologyVersionData! @@ -1977,6 +1998,16 @@ export interface NexusGenFieldTypeNames { name: 'String'; status: 'GraphEdgeStatus'; }; + MplsLspCount: { + // field return type name + counts: 'MplsLspCountItem'; + }; + MplsLspCountItem: { + // field return type name + incomingLsps: 'Int'; + outcomingLsps: 'Int'; + target: 'String'; + }; MplsTopology: { // field return type name edges: 'GraphEdge'; @@ -2146,6 +2177,7 @@ export interface NexusGenFieldTypeNames { kafkaHealthCheck: 'IsOkResponse'; labels: 'LabelConnection'; locations: 'LocationConnection'; + mplsLspCount: 'MplsLspCount'; mplsTopology: 'MplsTopology'; netTopology: 'NetTopology'; netTopologyVersionData: 'NetTopologyVersionData'; @@ -2566,6 +2598,10 @@ export interface NexusGenArgTypes { first?: number | null; // Int last?: number | null; // Int }; + mplsLspCount: { + // args + deviceId: string; // String! + }; netTopologyVersionData: { // args version: string; // String! diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 8fd13b21..d79321b4 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -930,3 +930,43 @@ export const MplsTopologyQuery = queryField('mplsTopology', { }; }, }); + +export const MplsLspCountItem = objectType({ + name: 'MplsLspCountItem', + definition: (t) => { + t.string('target'); + t.int('incomingLsps'); + t.int('outcomingLsps'); + }, +}); + +export const MplsTotalLspCount = objectType({ + name: 'MplsLspCount', + definition: (t) => { + t.list.field('counts', { type: MplsLspCountItem }); + }, +}); + +export const MplsLspCountQuery = queryField('mplsLspCount', { + type: 'MplsLspCount', + args: { + deviceId: nonNull(stringArg()), + }, + resolve: async (_, args, { topologyDiscoveryGraphQLAPI }) => { + const { deviceId } = args; + const nativeId = fromGraphId('GraphNode', deviceId); + const result = await topologyDiscoveryGraphQLAPI?.getMplsLspCount(nativeId); + + if (!result?.mplsLspCount) { + return null; + } + + return { + counts: result.mplsLspCount.map((c) => ({ + target: c?.to_device ?? null, + incomingLsps: c?.incoming_lsps ?? null, + outcomingLsps: c?.outcoming_lsps ?? null, + })), + }; + }, +}); From 6a1b0a6c65df305345e4ef4428d08a87e37b6fad Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Wed, 21 Aug 2024 11:08:23 +0200 Subject: [PATCH 11/18] FR-317 mpls topology version (#464) * mpls topology version query added * handle missing interfaces --- .../topology-discovery.graphql.ts | 42 ++- .../topology-discovery-graphql.ts | 1 + src/external-api/topology-network-types.ts | 53 +++- src/helpers/topology.helpers.ts | 279 ++++++++++++++++++ src/schema/api.graphql | 6 + src/schema/nexus-typegen.ts | 21 ++ src/schema/topology.ts | 47 ++- 7 files changed, 442 insertions(+), 7 deletions(-) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index ac1e4008..4f4158dc 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -76,7 +76,7 @@ export type DeviceGeoLocation = { __typename?: 'DeviceGeoLocation'; /** Defining the area around the device, with four elements indicating its boundaries. */ bbox: Maybe>>; - /** Device location coordinates providing latitude and longitude. */ + /** Device location coordinates providing longitude and latitude (in this order, based on GeoJSON convention). */ coordinates: Array; /** Type of geometry. */ type: GeometryType; @@ -112,10 +112,44 @@ export type DeviceMetadataEdge = { node: Maybe; }; -/** Filter for Metadata device type based on device name. */ +/** Filter for Metadata device type based on device name, or other attributes. */ export type DeviceMetadataFilter = { /** Regex of device name. */ deviceName?: InputMaybe; + /** + * A GeoJSON Polygon shape used for filtering devices based on their location in this area. + * + * The GeoJSON Polygon consists of a series of closed LineString objects (ring-like). + * These Linear Ring objects consist of four or more coordinate pairs with the first and last coordinate + * pair being equal. Coordinate pairs of a Polygon are an array of linear ring coordinate arrays. + * The first element in the array represents the exterior ring. + * Any subsequent elements represent interior rings (holes within the surface). + * + * The orientation of the first linear ring is crucial: the right-hand-rule is applied, so that the area to the left + * of the path of the linear ring (when walking on the surface of the Earth) is considered to be the “interior” + * of the polygon. All other linear rings must be contained within this interior. + * + * Example with a hole: + * [ + * [ + * [100.0, 0.0], + * [101.0, 0.0], + * [101.0, 1.0], + * [100.0, 1.0], + * [100.0, 0.0] + * ], + * [ + * [100.8, 0.8], + * [100.8, 0.2], + * [100.2, 0.2], + * [100.2, 0.8], + * [100.8, 0.8] + * ] + * ] + */ + polygon?: InputMaybe>>>; + /** Topology in which device must be present. */ + topologyType?: InputMaybe; }; /** Type of geometry. */ @@ -1532,7 +1566,7 @@ export type DeviceMetadataQueryVariables = Exact<{ export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; -export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; +export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; export type MplsInterfaceDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } }; @@ -1541,7 +1575,7 @@ export type MplsInterfacePartsFragment = { __typename?: 'MplsInterface', id: str export type MplsTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; +export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; export type MplsLspCountQueryVariables = Exact<{ deviceId: Scalars['ID']; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 754f9e49..817b7792 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -460,6 +460,7 @@ const MPLS_TOPOLOGY = gql` } } details { + router_id mpls_data { lsp_id in_label diff --git a/src/external-api/topology-network-types.ts b/src/external-api/topology-network-types.ts index 803faba6..33802c83 100644 --- a/src/external-api/topology-network-types.ts +++ b/src/external-api/topology-network-types.ts @@ -47,6 +47,7 @@ const Interface = t.type({ }); const PtpInterface = t.intersection([Interface, t.type({ details: PtpInterfaceDetails })]); const SynceInterface = t.intersection([Interface, t.type({ details: SynceInterfaceDetails })]); +const MplsInterface = t.intersection([Interface, t.type({})]); const NetInterface = t.type({ _id: t.string, _key: t.string, @@ -81,6 +82,13 @@ const SynceInterfaceWithStatusValidator = t.intersection([ }), ]); +const MplsInterfaceWithStatusValidator = t.intersection([ + MplsInterface, + t.type({ + status: StatusValidator, + }), +]); + const CoordinatesValidator = t.type({ x: t.number, y: t.number, @@ -126,6 +134,17 @@ const SynceDeviceValidator = t.intersection([ }), ]); +const MplsDeviceValidator = t.intersection([ + InterfaceWithStatusValidator, + t.type({ + labels: t.array(t.string), + details: t.type({ + // selected_for_use: t.string, + }), + coordinates: CoordinatesValidator, + }), +]); + const NetworkValidator = t.type({ _id: t.string, _key: t.string, @@ -195,6 +214,11 @@ const ChangedSynceDevice = t.type({ old: SynceDeviceValidator, }); +const ChangedMplsDevice = t.type({ + new: MplsDeviceValidator, + old: MplsDeviceValidator, +}); + const ChangedNetDevice = t.type({ new: NetDeviceValidator, old: NetDeviceValidator, @@ -221,6 +245,10 @@ const ChangedSynceInterface = t.type({ new: SynceInterfaceWithStatusValidator, old: SynceInterfaceWithStatusValidator, }); +const ChangedMplsInterface = t.type({ + new: MplsInterfaceWithStatusValidator, + old: MplsInterfaceWithStatusValidator, +}); const ChangedNetInterface = t.type({ new: NetInterfaceValidator, old: NetInterfaceValidator, @@ -276,6 +304,20 @@ const ChangeNetDiff = t.type({ NetNetwork: t.array(ChangedNetNetwork), }); +const MplsDiff = t.type({ + MplsDevice: t.array(MplsDeviceValidator), + MplsHas: t.array(EdgeWithStatusValidator), + MplsInterface: t.array(MplsInterfaceWithStatusValidator), + MplsLink: t.array(Edge), +}); + +const ChangeMplsDiff = t.type({ + MplsDevice: t.array(ChangedMplsDevice), + MplsHas: t.array(ChangedEdgeWithStatusValidator), + MplsInterface: t.array(ChangedMplsInterface), + MplsLink: t.array(ChangedEdge), +}); + const NetDiff = t.type({ NetDevice: t.array(NetDeviceValidator), NetHas: t.array(Edge), @@ -284,8 +326,8 @@ const NetDiff = t.type({ NetNetwork: t.array(NetworkValidator), }); -const TopologyDiff = t.union([PhyDiff, PtpDiff, SynceDiff, NetDiff]); -const ChangeTopologyDiff = t.union([ChangePhyDiff, ChangePtpDiff, ChangeSynceDiff, ChangeNetDiff]); +const TopologyDiff = t.union([PhyDiff, PtpDiff, SynceDiff, NetDiff, MplsDiff]); +const ChangeTopologyDiff = t.union([ChangePhyDiff, ChangePtpDiff, ChangeSynceDiff, ChangeNetDiff, ChangeMplsDiff]); export type PhyTopologyDiffType = { added: t.TypeOf; @@ -305,6 +347,12 @@ export type SynceTopologyDiffType = { deleted: t.TypeOf; }; +export type MplsTopologyDiffType = { + added: t.TypeOf; + changed: t.TypeOf; + deleted: t.TypeOf; +}; + export type NetTopologyDiffType = { added: t.TypeOf; changed: t.TypeOf; @@ -348,6 +396,7 @@ export function decodeLinksAndDevicesOutput(value: unknown): LinksAndDevicesOutp export type InterfaceWithStatus = t.TypeOf; export type PtpInterfaceWithStatus = t.TypeOf; export type SynceInterfaceWithStatus = t.TypeOf; +export type MplsInterfaceWithStatus = t.TypeOf; const HasAndInterfacesOutputValidator = t.type({ has: t.array(EdgeWithStatusValidator), diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index 4a8eac83..8716dd9c 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -10,6 +10,10 @@ import { SynceDevice, SynceTopologyQuery, TopologyDevicesQuery, + MplsDevice, + MplsDeviceDetails as ApiMplsDeviceDetails, + LspTunnel as ApiLspTunnel, + MplsData as ApiMplsData, } from '../__generated__/topology-discovery.graphql'; import { ArangoDevice, @@ -27,6 +31,8 @@ import { SynceInterfaceWithStatus, ArangoNetDevice, NetInterface, + MplsInterfaceWithStatus, + MplsTopologyDiffType, } from '../external-api/topology-network-types'; import { omitNullValue, unwrap } from './utils.helpers'; import { toGraphId } from './id-helper'; @@ -60,6 +66,33 @@ type SynceDeviceDetails = { selectedForUse: string | null; }; +type Signalization = 'RSVP' | 'LDP'; + +type MplsData = { + lspId: string; + inputInterface: string | null; + inputlabel: number | null; + ldpPrefix: string | null; + mplsOperation: string | null; + operState: string | null; + outputInterface: string | null; + outputLabel: number | null; + signalization: Signalization | null; +}; + +type LspTunnel = { + lspId: string; + fromDevice: string | null; + toDevice: string | null; + signalization: Signalization; + uptime: number | null; +}; + +type MplsDeviceDetails = { + mplsData: MplsData[] | null; + lspTunnels: LspTunnel[] | null; +}; + function isPtpTopologyDiff(topology: TopologyDiffOutput): topology is PtpTopologyDiffType { const added = Object.keys(topology.added).every((key) => key.includes('Ptp')); const deleted = Object.keys(topology.deleted).every((key) => key.includes('Ptp')); @@ -74,6 +107,13 @@ function isSynceTopologyDiff(topology: TopologyDiffOutput): topology is SynceTop return added && deleted; } +function isMplsTopologyDiff(topology: TopologyDiffOutput): topology is MplsTopologyDiffType { + const added = Object.keys(topology.added).every((key) => key.includes('Mpls')); + const deleted = Object.keys(topology.deleted).every((key) => key.includes('Mpls')); + + return added && deleted; +} + function isNetTopologyDiff(topology: TopologyDiffOutput): topology is NetTopologyDiffType { const added = Object.keys(topology.added).every((key) => key.includes('Net')); const deleted = Object.keys(topology.deleted).every((key) => key.includes('Net')); @@ -108,6 +148,7 @@ type PtpDeviceWithoutInterfaces = Omit; +type MplsDeviceWithoutInterfaces = Omit; export function convertNetDeviceToArangoDevice(netDevice: QueryNetDevice): ArangoNetDevice | null { if (!netDevice.phyDevice) { @@ -320,6 +361,50 @@ export function convertSynceDeviceToArangoDevice(synceDevice: SynceDeviceWithout }; } +export type ArangoMplsDevice = Omit; + +function convertApiMplsDeviceDetailToMplsDeviceDetail(input: ApiMplsDeviceDetails): MplsDeviceDetails { + return { + lspTunnels: + input.lsp_tunnels + ?.filter((t): t is ApiLspTunnel => t != null) + .map((t) => ({ + lspId: t.lsp_id, + fromDevice: t.from_device, + toDevice: t.to_device, + signalization: t.signalisation, + uptime: t.uptime, + })) ?? null, + mplsData: + input.mpls_data + ?.filter((d): d is ApiMplsData => d != null) + .map((d) => ({ + lspId: d.lsp_id, + ldpPrefix: d.ldp_prefix, + inputlabel: d.in_label, + inputInterface: d.in_interface, + outputLabel: d.out_label, + outputInterface: d.out_interface, + mplsOperation: d.mpls_operation, + operState: d.oper_state, + signalization: d.signalisation, + })) ?? null, + }; +} + +export function convertMplsDeviceToArangoDevice(mplsDevice: MplsDeviceWithoutInterfaces): ArangoMplsDevice { + const { name, status, coordinates, details, labels } = mplsDevice; + return { + _id: mplsDevice.id, + _key: mplsDevice.id, + coordinates, + mplsDeviceDetails: convertApiMplsDeviceDetailToMplsDeviceDetail(details), + labels: labels ?? [], + name, + status, + }; +} + export function getNetNodesFromTopologyQuery(query: NetTopologyQuery): ArangoNetDevice[] { const nodes = query.netDevices.edges @@ -358,6 +443,15 @@ export function getSynceNodesFromTopologyQuery(query: SynceTopologyQuery): Arang return nodes; } +export function getMplsNodesFromTopologyQuery(query: MplsTopologyQuery): ArangoMplsDevice[] { + const nodes = + query.mplsDevices.edges + ?.map((e) => e?.node) + .filter(omitNullValue) + .map(convertMplsDeviceToArangoDevice) ?? []; + return nodes; +} + export function getEdgesFromTopologyQuery(query: TopologyDevicesQuery): ArangoEdge[] { const currentEdges = query.phyDevices.edges?.flatMap( (e) => @@ -431,6 +525,31 @@ export function getSynceEdgesFromTopologyQuery(query: SynceTopologyQuery): Arang return currentEdges ?? []; } +export function getMplsEdgesFromTopologyQuery(query: MplsTopologyQuery): ArangoEdge[] { + const currentEdges = query.mplsDevices.edges?.flatMap( + (e) => + e?.node?.mplsInterfaces.edges + ?.map((i) => i?.node) + .filter(omitNullValue) + .flatMap((i) => + i.mplsLinks?.edges?.map((mplsLinkEdge) => { + if (!mplsLinkEdge?.link || !mplsLinkEdge?.node) { + return null; + } + return { + _id: mplsLinkEdge.link, + _key: mplsLinkEdge.link, + _from: i.id, + _to: mplsLinkEdge.node.id, + }; + }), + ) + .filter(omitNullValue) ?? [], + ); + + return currentEdges ?? []; +} + export function getNetEdgesFromTopologyQuery(query: NetTopologyQuery): ArangoEdge[] { const currentEdges = query.netDevices.edges?.flatMap( (e) => @@ -526,6 +645,28 @@ export function getOldTopologyInterfaceEdges( ); } + if (isMplsTopologyDiff(diffData)) { + return ( + interfaceEdges + // filter has edges added to current topology + .filter((e) => !diffData.added.MplsHas.find((h) => e._id === h._id)) + // filter edges pointing to added interfaces + .filter((e) => !diffData.added.MplsInterface.find((i) => e._to === i._id)) + // filter edges pointing to added device + .filter((e) => !diffData.added.MplsDevice.find((d) => e._from === d._id)) + // add has edges removed from current topology + .concat(diffData.deleted.MplsHas) + // change `SynceHas` edges from old topology + .map((e) => { + const changedEdge = diffData.changed.MplsHas.find((h) => h.old._id === e._id); + if (!changedEdge) { + return e; + } + return changedEdge.old; + }) + ); + } + return []; } @@ -619,6 +760,26 @@ export function getOldTopologyConnectedEdges(connected: ArangoEdge[], diffData: ); } + if (isMplsTopologyDiff(diffData)) { + return ( + connected + // filter connected edges added to current topology + .filter((e) => !diffData.added.MplsLink.find((c) => e._id === c._id)) + // filter edges pointing to added interfaces + .filter((e) => !diffData.added.MplsInterface.find((i) => e._to === i._id)) + // add connected edges removed from current topology + .concat(diffData.deleted.MplsLink) + // change `SynceLink` edges from old topology + .map((e) => { + const changedEdge = diffData.changed.MplsLink.find((h) => h.old._id === e._id); + if (!changedEdge) { + return e; + } + return changedEdge.old; + }) + ); + } + if (isNetTopologyDiff(diffData)) { return ( connected @@ -747,6 +908,59 @@ export function getOldSynceDevicesTopology( return oldDevices; } +export function getOldMplsDevicesTopology( + devices: ArangoMplsDevice[], + diffData: TopologyDiffOutput, +): ArangoMplsDevice[] { + let oldDevices: ArangoMplsDevice[] = []; + + if (isMplsTopologyDiff(diffData)) { + oldDevices = devices + .map((d) => ({ + ...d, + mplsDeviceDetails: { + lspTunnels: [], + mplsData: [], + }, + })) + // filter devices added to current topology + .filter((n) => !diffData.added.MplsDevice.find((d) => n._id === d._id)) + // add devices removed from current topology + .concat( + diffData.deleted.MplsDevice.map((d) => ({ + ...d, + mplsDeviceDetails: { + lspTunnels: [], + mplsData: [], + }, + })), + ) + // change devices from old topology + .map((n) => { + const changedDevice = diffData.changed.MplsDevice.find((d) => d.old._id === n._id); + if (!changedDevice) { + return { + ...n, + mplsDeviceDetails: { + lspTunnels: [], + mplsData: [], + }, + }; + } + return { + ...changedDevice.old, + mplsDeviceDetails: { + lspTunnels: [], + mplsData: [], + }, + // synceDeviceDetails: { selectedForUse: changedDevice.old.details.selected_for_use }, + }; + }); + } + + return oldDevices; +} + export function getOldNetDevicesTopology(devices: ArangoNetDevice[], diffData: TopologyDiffOutput): ArangoNetDevice[] { let oldDevices: ArangoNetDevice[] = []; @@ -1096,6 +1310,22 @@ export function getSynceDeviceInterfaceEdges(topologyDevices: SynceTopologyQuery ); } +export function getMplsDeviceInterfaceEdges(topologyDevices: MplsTopologyQuery): ArangoEdgeWithStatus[] { + return ( + topologyDevices.mplsDevices.edges?.flatMap( + (d) => + d?.node?.mplsInterfaces.edges?.map((i) => ({ + _id: `${d.node?.id}-${i?.node?.id}`, + // _key: i?.node?.phyLink?.id ?? '', // INFO: idHas was removed + _key: 'some_id', + _from: d.node?.id ?? '', + _to: i?.node?.id ?? '', + status: d.node?.status ?? 'unknown', + })) ?? [], + ) ?? [] + ); +} + export function getNetDeviceInterfaceEdges(topologyDevices: NetTopologyQuery): ArangoEdgeWithStatus[] { return ( topologyDevices.netDevices.edges?.flatMap( @@ -1550,6 +1780,55 @@ export function makeNetTopologyDiff( }; } +export function makeMplsTopologyDiff( + topologyDiff: TopologyDiffOutput, + currentNodes: ArangoMplsDevice[], + currentEdges: ArangoEdge[], + interfaces: MplsInterfaceWithStatus[], + interfaceEdges: ArangoEdgeWithStatus[], +) { + const oldDevices = getOldMplsDevicesTopology(currentNodes, topologyDiff); + const oldInterfaceEdges = getOldTopologyInterfaceEdges(interfaceEdges, topologyDiff); + + const interfaceDeviceMap = makeInterfaceDeviceMap(oldInterfaceEdges); + const interfaceNameMap = makeInterfaceNameMap( + [...interfaces, ...getTopologyDiffInterfaces(topologyDiff)], + (i) => i.name, + ); + const interfaceMap = makeInterfaceMap(oldInterfaceEdges, interfaceNameMap); + const nodesMap = makeNodesMap(oldDevices, (d) => d.name); + + const oldEdges = getOldTopologyConnectedEdges(currentEdges, topologyDiff) + .map((e) => ({ + id: e._id, + source: { + interface: e._from, + nodeId: nodesMap[interfaceDeviceMap[e._from]], + }, + target: { + interface: e._to, + nodeId: nodesMap[interfaceDeviceMap[e._to]], + }, + })) + .filter((e) => e.source.nodeId != null && e.target.nodeId != null); + + return { + nodes: oldDevices.map((device) => ({ + id: toGraphId('GraphNode', device._id), + name: device.name, + interfaces: + interfaceMap[device._id]?.map((intf) => ({ + ...intf, + })) ?? [], + coordinates: device.coordinates, + mplsDeviceDetails: device.mplsDeviceDetails, + nodeId: device._id, + status: device.status, + })), + edges: oldEdges, + }; +} + export function convertDeviceMetadataToMapNodes( deviceMetadataQuery: DeviceMetadataQuery, deviceLocationMap: Map, diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 6fde991e..9b2b24a8 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -559,6 +559,11 @@ type MplsTopology { nodes: [MplsGraphNode!]! } +type MplsTopologyVersionData { + edges: [GraphVersionEdge!]! + nodes: [MplsGraphNode!]! +} + type Mutation { activateStream(id: String!): ActivateStreamPayload! addBlueprint(input: AddBlueprintInput!): AddBlueprintPayload! @@ -739,6 +744,7 @@ type Query { locations(after: String, before: String, first: Int, last: Int): LocationConnection! mplsLspCount(deviceId: String!): MplsLspCount mplsTopology: MplsTopology + mplsTopologyVersionData(version: String!): MplsTopologyVersionData! netTopology: NetTopology netTopologyVersionData(version: String!): NetTopologyVersionData! node(id: ID!): Node diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 04fdbd3f..5d7d666b 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -582,6 +582,11 @@ export interface NexusGenObjects { edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! }; + MplsTopologyVersionData: { + // root type + edges: NexusGenRootTypes['GraphVersionEdge'][]; // [GraphVersionEdge!]! + nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! + }; Mutation: {}; NetInterface: { // root type @@ -1253,6 +1258,11 @@ export interface NexusGenFieldTypes { edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! }; + MplsTopologyVersionData: { + // field return type + edges: NexusGenRootTypes['GraphVersionEdge'][]; // [GraphVersionEdge!]! + nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! + }; Mutation: { // field return type activateStream: NexusGenRootTypes['ActivateStreamPayload']; // ActivateStreamPayload! @@ -1419,6 +1429,7 @@ export interface NexusGenFieldTypes { locations: NexusGenRootTypes['LocationConnection']; // LocationConnection! mplsLspCount: NexusGenRootTypes['MplsLspCount'] | null; // MplsLspCount mplsTopology: NexusGenRootTypes['MplsTopology'] | null; // MplsTopology + mplsTopologyVersionData: NexusGenRootTypes['MplsTopologyVersionData']; // MplsTopologyVersionData! netTopology: NexusGenRootTypes['NetTopology'] | null; // NetTopology netTopologyVersionData: NexusGenRootTypes['NetTopologyVersionData']; // NetTopologyVersionData! node: NexusGenRootTypes['Node'] | null; // Node @@ -2013,6 +2024,11 @@ export interface NexusGenFieldTypeNames { edges: 'GraphEdge'; nodes: 'MplsGraphNode'; }; + MplsTopologyVersionData: { + // field return type name + edges: 'GraphVersionEdge'; + nodes: 'MplsGraphNode'; + }; Mutation: { // field return type name activateStream: 'ActivateStreamPayload'; @@ -2179,6 +2195,7 @@ export interface NexusGenFieldTypeNames { locations: 'LocationConnection'; mplsLspCount: 'MplsLspCount'; mplsTopology: 'MplsTopology'; + mplsTopologyVersionData: 'MplsTopologyVersionData'; netTopology: 'NetTopology'; netTopologyVersionData: 'NetTopologyVersionData'; node: 'Node'; @@ -2602,6 +2619,10 @@ export interface NexusGenArgTypes { // args deviceId: string; // String! }; + mplsTopologyVersionData: { + // args + version: string; // String! + }; netTopologyVersionData: { // args version: string; // String! diff --git a/src/schema/topology.ts b/src/schema/topology.ts index d79321b4..7d1e4414 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -8,7 +8,6 @@ import { enumType, interfaceType, queryField, - arg, } from 'nexus'; import config from '../config'; import { fromGraphId } from '../helpers/id-helper'; @@ -17,6 +16,10 @@ import { getDeviceInterfaceEdges, getEdgesFromTopologyQuery, getFilterQuery, + getMplsDeviceInterfaceEdges, + getMplsEdgesFromTopologyQuery, + getMplsNodesFromTopologyQuery, + getMplsTopologyInterfaces, getNetDeviceInterfaceEdges, getNetEdgesFromTopologyQuery, getNetNodesFromTopologyQuery, @@ -32,6 +35,7 @@ import { getSynceNodesFromTopologyQuery, getSynceTopologyInterfaces, getTopologyInterfaces, + makeMplsTopologyDiff, makeMplsTopologyEdges, makeMplsTopologyNodes, makeNetTopologyDiff, @@ -337,6 +341,14 @@ export const SynceTopologyVersionData = objectType({ }, }); +export const MplsTopologyVersionData = objectType({ + name: 'MplsTopologyVersionData', + definition: (t) => { + t.nonNull.list.field('nodes', { type: nonNull(MplsGraphNode) }); + t.nonNull.list.field('edges', { type: nonNull(GraphVersionEdge) }); + }, +}); + export const NetInterface = objectType({ name: 'NetInterface', definition: (t) => { @@ -571,6 +583,39 @@ export const TopologyVersionDataQuery = extendType({ }, }); +export const MplsTopologyVersionDataQuery = extendType({ + type: 'Query', + definition: (t) => { + t.nonNull.field('mplsTopologyVersionData', { + type: MplsTopologyVersionData, + args: { + version: nonNull(stringArg()), + }, + resolve: async (_, args, { topologyDiscoveryGraphQLAPI }) => { + if (!config.topologyEnabled || !topologyDiscoveryGraphQLAPI) { + return { + nodes: [], + edges: [], + }; + } + + const topologyDevicesResult = await topologyDiscoveryGraphQLAPI.getMplsTopology(); + + const currentNodes = getMplsNodesFromTopologyQuery(topologyDevicesResult); + const currentEdges = getMplsEdgesFromTopologyQuery(topologyDevicesResult); + + const interfaces = getMplsTopologyInterfaces(topologyDevicesResult).map((i) => ({ ...i, _key: i.id })); + const interfaceEdges = getMplsDeviceInterfaceEdges(topologyDevicesResult); + + const { version } = args; + const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'MplsTopology'); + + return makeMplsTopologyDiff(topologyDiff, currentNodes, currentEdges, interfaces, interfaceEdges); + }, + }); + }, +}); + export const GraphNodeCoordinatesInput = inputObjectType({ name: 'GraphNodeCoordinatesInput', definition: (t) => { From f3784871a9278ce19e25084530ea62fa2dcbed80 Mon Sep 17 00:00:00 2001 From: plehocky <117287338+plehocky@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:54:20 +0200 Subject: [PATCH 12/18] FR-329 - topology overlay integration (#465) * adjusted metadata filter and phyDeviceName on netTopology query * lint fix --- src/external-api/topology-discovery-graphql.ts | 16 +++++++++++++++- src/helpers/topology.helpers.ts | 1 + src/schema/api.graphql | 1 + src/schema/nexus-typegen.ts | 3 +++ src/schema/topology.ts | 1 + 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 817b7792..1c27590a 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -664,8 +664,22 @@ function getTopologyDiscoveryApi() { } async function getDeviceMetadata(filters?: DeviceMetadataFilters): Promise { + const filter: DeviceMetadataFilters = {}; + + if (filters?.deviceName != null) { + filter.deviceName = filters.deviceName; + } + + if (filters?.topologyType != null) { + filter.topologyType = filters.topologyType; + } + + if (filters?.polygon != null) { + filter.polygon = filters.polygon; + } + const response = await client.request(DEVICE_METADATA, { - filters, + filters: filter, }); return response; } diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index 8716dd9c..bc491179 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -1391,6 +1391,7 @@ export function makeNetTopologyNodes(netTopologyDevices?: NetTopologyQuery) { } return { id: toGraphId('GraphNode', node.id), + phyDeviceName: node.phyDevice?.name, nodeId: node.id, name: node.routerId, interfaces: diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 9b2b24a8..ede7cbc8 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -622,6 +622,7 @@ type NetNode { name: String! networks: [NetNetwork!]! nodeId: String! + phyDeviceName: String } type NetRoutingPathNode { diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 5d7d666b..184cb6ae 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -607,6 +607,7 @@ export interface NexusGenObjects { name: string; // String! networks: NexusGenRootTypes['NetNetwork'][]; // [NetNetwork!]! nodeId: string; // String! + phyDeviceName?: string | null; // String }; NetRoutingPathNode: { // root type @@ -1322,6 +1323,7 @@ export interface NexusGenFieldTypes { name: string; // String! networks: NexusGenRootTypes['NetNetwork'][]; // [NetNetwork!]! nodeId: string; // String! + phyDeviceName: string | null; // String }; NetRoutingPathNode: { // field return type @@ -2088,6 +2090,7 @@ export interface NexusGenFieldTypeNames { name: 'String'; networks: 'NetNetwork'; nodeId: 'String'; + phyDeviceName: 'String'; }; NetRoutingPathNode: { // field return type name diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 7d1e4414..a0d2c0b3 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -690,6 +690,7 @@ export const NetNode = objectType({ t.nonNull.id('id'); t.nonNull.string('nodeId'); t.nonNull.string('name'); + t.string('phyDeviceName'); t.nonNull.list.nonNull.field('interfaces', { type: nonNull(NetInterface) }); t.nonNull.list.nonNull.field('networks', { type: nonNull(NetNetwork) }); t.nonNull.field('coordinates', { type: GraphNodeCoordinates }); From e6956b9ba829a69895175d1d0e6567fa5b616f6c Mon Sep 17 00:00:00 2001 From: plehocky <117287338+plehocky@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:17:16 +0200 Subject: [PATCH 13/18] added neighbor query for map topology devices (#466) --- .../topology-discovery.graphql.ts | 149 ++++++++++++++++++ .../topology-discovery-graphql.ts | 21 +++ src/schema/api.graphql | 15 ++ src/schema/nexus-typegen.ts | 38 +++++ src/schema/topology.ts | 54 +++++++ 5 files changed, 277 insertions(+) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index 4f4158dc..690cbb10 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -434,6 +434,15 @@ export type MutationUpdateNodeStatusArgs = { topology_type?: InputMaybe; }; +/** Metadata information about a neighbor device. */ +export type Neighbor = { + __typename?: 'Neighbor'; + /** Identifier of the neighbor device document (for example, MplsDevice/1). */ + device_id: Scalars['String']; + /** Human-readable name of the neighbor device (for example, CPE_01). */ + device_name: Scalars['String']; +}; + /** Representation of the routing entity in the network topology. */ export type NetDevice = Node & { __typename?: 'NetDevice'; @@ -1067,6 +1076,8 @@ export type Query = { * Also return the count of incoming and outcoming tunnels from / to that device. */ mplsLspCount: Maybe>>; + /** Find identifiers of all neighbour devices of the specified device in the specified topology. */ + neighbors: Maybe>; /** Read network devices that match specified filter. */ netDevices: NetDeviceConnection; /** @@ -1114,6 +1125,13 @@ export type Query = { * Only documents that belong to the specified topology are included in the diff. */ topologyDiff: TopologyResponse; + /** + * Returns an overlay between two topologies. + * The overlay works in such a way that it takes the first topology, and tries to find devices / interfaces + * from the first topology in the second one. + * The first topology can be taken as a reference topology. The second topology is joined to the first one (similar to LEFT JOIN in databases) + */ + topologyOverlay: TopologyOverlayDeviceConnection; }; @@ -1143,6 +1161,12 @@ export type QueryMplsLspCountArgs = { }; +export type QueryNeighborsArgs = { + device_name: Scalars['String']; + topology_type: TopologyType; +}; + + export type QueryNetDevicesArgs = { cursor?: InputMaybe; filters?: InputMaybe; @@ -1213,6 +1237,15 @@ export type QueryTopologyDiffArgs = { old_db: Scalars['String']; }; + +export type QueryTopologyOverlayArgs = { + cursor?: InputMaybe; + filters?: InputMaybe; + first?: InputMaybe; + firstTopology: TopologyType; + secondTopology: TopologyType; +}; + /** Computed routing path from source to destination device. */ export type RoutingPath = { __typename?: 'RoutingPath'; @@ -1448,6 +1481,114 @@ export type SyncePathOutputCollections = /** Include SynceInterface nodes in the returned path. */ | 'SynceInterface'; +export type TopologyOverlayDevice = { + __typename?: 'TopologyOverlayDevice'; + /** Unique identifier of the object. */ + id: Scalars['ID']; + /** Device name. */ + name: Scalars['String']; + /** Document device ID from the second topology (can be null). */ + secondTopologyId: Maybe; + /** List of ports that are present on the device. */ + topologyOverlayInterfaces: TopologyOverlayInterfaceConnection; +}; + + +export type TopologyOverlayDeviceTopologyOverlayInterfacesArgs = { + cursor?: InputMaybe; + filters?: InputMaybe; + first?: InputMaybe; +}; + +/** Grouped list of TopologyOverlayDevice objects and pagination metadata. */ +export type TopologyOverlayDeviceConnection = { + __typename?: 'TopologyOverlayDeviceConnection'; + /** List of TopologyOverlayDeviceEdge objects. */ + edges: Maybe>>; + /** Pagination metadata. */ + pageInfo: PageInfo; +}; + +/** Grouped TopologyOverlayDeviceEdge object and associated cursor used by pagination. */ +export type TopologyOverlayDeviceEdge = { + __typename?: 'TopologyOverlayDeviceEdge'; + /** Pagination cursor for this edge. */ + cursor: Scalars['String']; + /** The associated TopologyOverlayDevice object. */ + node: Maybe; +}; + +/** Filter for TopologyOverlayDevice type based on device name. */ +export type TopologyOverlayDeviceFilter = { + /** Regex of device name. */ + name?: InputMaybe; +}; + +export type TopologyOverlayInterface = { + __typename?: 'TopologyOverlayInterface'; + /** Document interface ID from the first topology */ + id: Scalars['ID']; + /** Interface name. */ + name: Scalars['String']; + /** Document device ID from the second topology (can be null). */ + secondTopologyId: Maybe; + /** Topology overlay device that owns this interface. */ + topologyOverlayDevice: Maybe; + /** Topology overlay neighbor interface */ + topologyOverlayLinks: Maybe; +}; + +/** Grouped list of TopologyOverlayInterface objects and pagination metadata. */ +export type TopologyOverlayInterfaceConnection = { + __typename?: 'TopologyOverlayInterfaceConnection'; + /** List of TopologyOverlayInterface objects. */ + edges: Maybe>>; + /** Pagination metadata. */ + pageInfo: PageInfo; +}; + +/** Grouped TopologyOverlayInterface object and associated cursor used by pagination. */ +export type TopologyOverlayInterfaceEdge = { + __typename?: 'TopologyOverlayInterfaceEdge'; + /** Pagination cursor for this edge. */ + cursor: Scalars['String']; + /** The associated TopologyOverlayInterface object. */ + node: Maybe; +}; + +/** Filter for TopologyOverlayInterface type based on the name of the device. */ +export type TopologyOverlayInterfaceFilter = { + /** Regex of interface name. */ + name?: InputMaybe; +}; + +/** Grouped list of TopologyOverlayLinks objects and pagination metadata. */ +export type TopologyOverlayLinkConnection = { + __typename?: 'TopologyOverlayLinkConnection'; + /** List of TopologyOverlayInterface objects. */ + edges: Maybe>>; + /** Pagination metadata. */ + pageInfo: PageInfo; +}; + +export type TopologyOverlayLinkEdge = { + __typename?: 'TopologyOverlayLinkEdge'; + /** Pagination cursor for this edge. */ + cursor: Scalars['String']; + /** Identifier of the link that connects this interface to the interface on the remote device */ + link: Maybe; + /** The associated TopologyOverlayInterface object. */ + node: Maybe; +}; + +export type TopologyOverlayLinkIds = { + __typename?: 'TopologyOverlayLinkIds'; + /** Identifier of the link that connects this interface to the interface on the remote device on the first topology. */ + firstTopologyLinkId: Scalars['ID']; + /** Identifier of the link that connects this interface to the interface on the remote device on the second topology. */ + secondTopologyLinkId: Maybe; +}; + /** Response from the topologyDiff query that contains diff between two databases. */ export type TopologyResponse = { __typename?: 'TopologyResponse'; @@ -1583,3 +1724,11 @@ export type MplsLspCountQueryVariables = Exact<{ export type MplsLspCountQuery = { __typename?: 'Query', mplsLspCount: Array<{ __typename?: 'MplsTotalLsps', to_device: string | null, incoming_lsps: number | null, outcoming_lsps: number | null } | null> | null }; + +export type NeighborsQueryVariables = Exact<{ + deviceName: Scalars['String']; + topologyType: TopologyType; +}>; + + +export type NeighborsQuery = { __typename?: 'Query', neighbors: Array<{ __typename?: 'Neighbor', device_id: string, device_name: string }> | null }; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 1c27590a..f8606c92 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -26,6 +26,8 @@ import { MplsTopologyQuery, MplsLspCountQuery, MplsLspCountQueryVariables, + NeighborsQuery, + NeighborsQueryVariables, } from '../__generated__/topology-discovery.graphql'; import { TopologyDiffOutput, decodeTopologyDiffOutput } from './topology-network-types'; @@ -550,6 +552,15 @@ const MPLS_LSP_COUNT = gql` } `; +const MAP_NEIGHBORS = gql` + query Neighbors($deviceName: String!, $topologyType: TopologyType!) { + neighbors(device_name: $deviceName, topology_type: $topologyType) { + device_id + device_name + } + } +`; + function getTopologyDiscoveryApi() { if (!config.topologyEnabled) { return undefined; @@ -652,6 +663,15 @@ function getTopologyDiscoveryApi() { return response; } + async function getMapNeighbors(deviceName: string, topologyType: TopologyType): Promise { + const response = await client.request(MAP_NEIGHBORS, { + deviceName, + topologyType, + }); + + return response; + } + async function getSyncePathToGrandMaster(deviceFrom: string): Promise { const response = await client.request( SYNCE_PATH, @@ -714,6 +734,7 @@ function getTopologyDiscoveryApi() { getDeviceMetadata, getMplsTopology, getMplsLspCount, + getMapNeighbors, }; } diff --git a/src/schema/api.graphql b/src/schema/api.graphql index ede7cbc8..6612f859 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -308,6 +308,10 @@ type DeviceMetadata { nodes: [GeoMapDevice] } +type DeviceNeighbors { + neighbors: [Neighbor] +} + input DeviceOrderByInput { direction: SortDirection! sortKey: SortDeviceBy! @@ -376,6 +380,11 @@ input FilterLabelsInput { name: String! } +input FilterNeighborInput { + deviceName: String! + topologyType: TopologyType! +} + input FilterStreamsInput { deviceName: String labels: [String!] @@ -604,6 +613,11 @@ type Mutation { updateStream(id: String!, input: UpdateStreamInput!): UpdateStreamPayload! } +type Neighbor { + deviceId: String! + deviceName: String! +} + type NetInterface { id: String! name: String! @@ -732,6 +746,7 @@ type Query { countries(after: String, before: String, first: Int, last: Int): CountryConnection! dataStore(deviceId: String!, transactionId: String!): DataStore deviceMetadata(filter: FilterDevicesMetadatasInput): DeviceMetadata + deviceNeighbor(filter: FilterNeighborInput): DeviceNeighbors devices( after: String before: String diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 184cb6ae..4b7bb728 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -143,6 +143,11 @@ export interface NexusGenInputs { // input type name: string; // String! }; + FilterNeighborInput: { + // input type + deviceName: string; // String! + topologyType: NexusGenEnums['TopologyType']; // TopologyType! + }; FilterStreamsInput: { // input type deviceName?: string | null; // String @@ -409,6 +414,10 @@ export interface NexusGenObjects { // root type nodes?: Array | null; // [GeoMapDevice] }; + DeviceNeighbors: { + // root type + neighbors?: Array | null; // [Neighbor] + }; DeviceStatus: { // root type deviceName?: string | null; // String @@ -588,6 +597,11 @@ export interface NexusGenObjects { nodes: NexusGenRootTypes['MplsGraphNode'][]; // [MplsGraphNode!]! }; Mutation: {}; + Neighbor: { + // root type + deviceId: string; // String! + deviceName: string; // String! + }; NetInterface: { // root type id: string; // String! @@ -1071,6 +1085,10 @@ export interface NexusGenFieldTypes { // field return type nodes: Array | null; // [GeoMapDevice] }; + DeviceNeighbors: { + // field return type + neighbors: Array | null; // [Neighbor] + }; DeviceStatus: { // field return type deviceName: string | null; // String @@ -1304,6 +1322,11 @@ export interface NexusGenFieldTypes { updateLocation: NexusGenRootTypes['UpdateLocationPayload']; // UpdateLocationPayload! updateStream: NexusGenRootTypes['UpdateStreamPayload']; // UpdateStreamPayload! }; + Neighbor: { + // field return type + deviceId: string; // String! + deviceName: string; // String! + }; NetInterface: { // field return type id: string; // String! @@ -1425,6 +1448,7 @@ export interface NexusGenFieldTypes { countries: NexusGenRootTypes['CountryConnection']; // CountryConnection! dataStore: NexusGenRootTypes['DataStore'] | null; // DataStore deviceMetadata: NexusGenRootTypes['DeviceMetadata'] | null; // DeviceMetadata + deviceNeighbor: NexusGenRootTypes['DeviceNeighbors'] | null; // DeviceNeighbors devices: NexusGenRootTypes['DeviceConnection']; // DeviceConnection! kafkaHealthCheck: NexusGenRootTypes['IsOkResponse'] | null; // IsOkResponse labels: NexusGenRootTypes['LabelConnection']; // LabelConnection! @@ -1838,6 +1862,10 @@ export interface NexusGenFieldTypeNames { // field return type name nodes: 'GeoMapDevice'; }; + DeviceNeighbors: { + // field return type name + neighbors: 'Neighbor'; + }; DeviceStatus: { // field return type name deviceName: 'String'; @@ -2071,6 +2099,11 @@ export interface NexusGenFieldTypeNames { updateLocation: 'UpdateLocationPayload'; updateStream: 'UpdateStreamPayload'; }; + Neighbor: { + // field return type name + deviceId: 'String'; + deviceName: 'String'; + }; NetInterface: { // field return type name id: 'String'; @@ -2192,6 +2225,7 @@ export interface NexusGenFieldTypeNames { countries: 'CountryConnection'; dataStore: 'DataStore'; deviceMetadata: 'DeviceMetadata'; + deviceNeighbor: 'DeviceNeighbors'; devices: 'DeviceConnection'; kafkaHealthCheck: 'IsOkResponse'; labels: 'LabelConnection'; @@ -2594,6 +2628,10 @@ export interface NexusGenArgTypes { // args filter?: NexusGenInputs['FilterDevicesMetadatasInput'] | null; // FilterDevicesMetadatasInput }; + deviceNeighbor: { + // args + filter?: NexusGenInputs['FilterNeighborInput'] | null; // FilterNeighborInput + }; devices: { // args after?: string | null; // String diff --git a/src/schema/topology.ts b/src/schema/topology.ts index a0d2c0b3..cb79ee88 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -954,6 +954,60 @@ export const deviceMetadataQuery = queryField('deviceMetadata', { }, }); +export const Neighbor = objectType({ + name: 'Neighbor', + definition: (t) => { + t.nonNull.string('deviceName'); + t.nonNull.string('deviceId'); + }, +}); + +export const DeviceNeighbors = objectType({ + name: 'DeviceNeighbors', + definition: (t) => { + t.list.field('neighbors', { + type: Neighbor, + }); + }, +}); + +export const FilterNeighborInput = inputObjectType({ + name: 'FilterNeighborInput', + definition: (t) => { + t.nonNull.string('deviceName'); + t.nonNull.field('topologyType', { type: TopologyType }); + }, +}); + +export const deviceNeighborQuery = queryField('deviceNeighbor', { + type: DeviceNeighbors, + args: { + filter: FilterNeighborInput, + }, + resolve: async (_, { filter }, { topologyDiscoveryGraphQLAPI }) => { + if (!topologyDiscoveryGraphQLAPI) { + return null; + } + + if (filter?.deviceName) { + const deviceNeighborsResult = await topologyDiscoveryGraphQLAPI.getMapNeighbors( + filter?.deviceName, + filter?.topologyType, + ); + + const neighborDevices = + deviceNeighborsResult.neighbors?.map((device) => ({ + deviceName: device.device_name, + deviceId: device.device_id, + })) || []; + + return { neighbors: neighborDevices }; + } + + return null; + }, +}); + export const MplsTopology = objectType({ name: 'MplsTopology', definition: (t) => { From 538f0a84ff9945cda4b79b7321863fa4271595b3 Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Mon, 2 Sep 2024 12:20:12 +0200 Subject: [PATCH 14/18] FR-312 lsp path (#467) * lsp path query added * connect to real backend lsp path endpint --- .../topology-discovery.graphql.ts | 51 ++++++++++++++++--- .../topology-discovery-graphql.ts | 26 +++++++++- src/helpers/topology.helpers.ts | 19 +++++-- src/schema/api.graphql | 13 +++++ src/schema/nexus-typegen.ts | 43 ++++++++++++++++ src/schema/topology.ts | 37 ++++++++++++++ 6 files changed, 178 insertions(+), 11 deletions(-) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index 690cbb10..a9cb536e 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -195,8 +195,6 @@ export type MplsData = { in_interface: Maybe; /** The input label. */ in_label: Maybe; - /** Where the tunnel is headed (Router IP.) */ - ldp_prefix: Maybe; /** Name of the link state packet. */ lsp_id: Scalars['String']; /** The operation type. */ @@ -340,6 +338,26 @@ export type MplsLinkEdge = { node: Maybe; }; +export type MplsLspMetadata = { + __typename?: 'MplsLspMetadata'; + /** From which device is the tunnel originating. */ + from_device: Scalars['String']; + /** Type of signalisation. */ + signalisation: Scalars['String']; + /** Where is the tunnel headed. */ + to_device: Scalars['String']; + /** Uptime of the tunnel in seconds. */ + uptime: Maybe; +}; + +export type MplsLspPath = { + __typename?: 'MplsLspPath'; + /** LSP metadata. */ + lsp_metadata: Maybe; + /** Ordered list of link IDs & device IDs on the path. */ + path: Maybe>>; +}; + export type MplsOperation = | 'Noop' | 'Pop' @@ -417,7 +435,7 @@ export type MutationEnableRemoteDebugSessionArgs = { export type MutationSyncArgs = { devices?: InputMaybe>>; labels?: InputMaybe>>; - provider_name: Scalars['String']; + topology_type: TopologyType; }; @@ -1076,6 +1094,11 @@ export type Query = { * Also return the count of incoming and outcoming tunnels from / to that device. */ mplsLspCount: Maybe>>; + /** + * Finds a LSP path between two devices based on start device ID and LSP ID. + * Also return MPLS LSP metadata about start device if they exist. + */ + mplsLspPath: MplsLspPath; /** Find identifiers of all neighbour devices of the specified device in the specified topology. */ neighbors: Maybe>; /** Read network devices that match specified filter. */ @@ -1095,7 +1118,7 @@ export type Query = { /** Read list of support device types in the specified topology. */ provider: ProviderResponse; /** Read list of available topology providers (e.g. physical, etp, eth_sync, etc.). */ - providers: Array; + providers: Array; /** Read ptp devices that match specified filter. */ ptpDevices: PtpDeviceConnection; /** @@ -1161,6 +1184,12 @@ export type QueryMplsLspCountArgs = { }; +export type QueryMplsLspPathArgs = { + device_id: Scalars['ID']; + lsp_id: Scalars['ID']; +}; + + export type QueryNeighborsArgs = { device_name: Scalars['String']; topology_type: TopologyType; @@ -1194,7 +1223,7 @@ export type QueryPhyDevicesArgs = { export type QueryProviderArgs = { - name: Scalars['String']; + topology_type: TopologyType; }; @@ -1707,7 +1736,7 @@ export type DeviceMetadataQueryVariables = Exact<{ export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; -export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; +export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; export type MplsInterfaceDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } }; @@ -1716,7 +1745,7 @@ export type MplsInterfacePartsFragment = { __typename?: 'MplsInterface', id: str export type MplsTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, ldp_prefix: string | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; +export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; export type MplsLspCountQueryVariables = Exact<{ deviceId: Scalars['ID']; @@ -1725,6 +1754,14 @@ export type MplsLspCountQueryVariables = Exact<{ export type MplsLspCountQuery = { __typename?: 'Query', mplsLspCount: Array<{ __typename?: 'MplsTotalLsps', to_device: string | null, incoming_lsps: number | null, outcoming_lsps: number | null } | null> | null }; +export type MplsPathQueryVariables = Exact<{ + deviceId: Scalars['ID']; + lspId: Scalars['ID']; +}>; + + +export type MplsPathQuery = { __typename?: 'Query', mplsLspPath: { __typename?: 'MplsLspPath', path: Array | null, lsp_metadata: { __typename?: 'MplsLspMetadata', from_device: string, to_device: string, uptime: number | null, signalisation: string } | null } }; + export type NeighborsQueryVariables = Exact<{ deviceName: Scalars['String']; topologyType: TopologyType; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index f8606c92..56dc70f8 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -26,6 +26,8 @@ import { MplsTopologyQuery, MplsLspCountQuery, MplsLspCountQueryVariables, + MplsPathQuery, + MplsPathQueryVariables, NeighborsQuery, NeighborsQueryVariables, } from '../__generated__/topology-discovery.graphql'; @@ -469,7 +471,6 @@ const MPLS_TOPOLOGY = gql` in_interface out_interface out_label - ldp_prefix mpls_operation oper_state signalisation @@ -552,6 +553,20 @@ const MPLS_LSP_COUNT = gql` } `; +const MPLS_LSP_PATH = gql` + query MplsPath($deviceId: ID!, $lspId: ID!) { + mplsLspPath(device_id: $deviceId, lsp_id: $lspId) { + path + lsp_metadata { + from_device + to_device + uptime + signalisation + } + } + } +`; + const MAP_NEIGHBORS = gql` query Neighbors($deviceName: String!, $topologyType: TopologyType!) { neighbors(device_name: $deviceName, topology_type: $topologyType) { @@ -718,6 +733,14 @@ function getTopologyDiscoveryApi() { return response; } + async function getLspPath(deviceId: string, lspId: string): Promise { + const response = await client.request(MPLS_LSP_PATH, { + deviceId, + lspId, + }); + return response; + } + return { getTopologyDevices, getPtpDiffSynce, @@ -734,6 +757,7 @@ function getTopologyDiscoveryApi() { getDeviceMetadata, getMplsTopology, getMplsLspCount, + getLspPath, getMapNeighbors, }; } diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index bc491179..62e16288 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -14,6 +14,7 @@ import { MplsDeviceDetails as ApiMplsDeviceDetails, LspTunnel as ApiLspTunnel, MplsData as ApiMplsData, + MplsPathQuery, } from '../__generated__/topology-discovery.graphql'; import { ArangoDevice, @@ -72,7 +73,6 @@ type MplsData = { lspId: string; inputInterface: string | null; inputlabel: number | null; - ldpPrefix: string | null; mplsOperation: string | null; operState: string | null; outputInterface: string | null; @@ -380,7 +380,6 @@ function convertApiMplsDeviceDetailToMplsDeviceDetail(input: ApiMplsDeviceDetail ?.filter((d): d is ApiMplsData => d != null) .map((d) => ({ lspId: d.lsp_id, - ldpPrefix: d.ldp_prefix, inputlabel: d.in_label, inputInterface: d.in_interface, outputLabel: d.out_label, @@ -1914,7 +1913,6 @@ export function makeMplsTopologyNodes(mplsDevices?: MplsTopologyQuery) { outputInterface: d?.out_interface ?? null, outputLabel: d?.out_label ?? null, operState: d?.oper_state ?? null, - ldpPrefix: d?.ldp_prefix ?? null, mplsOperation: d?.mpls_operation ?? null, })) ?? null, }, @@ -1979,3 +1977,18 @@ export function makeMplsTopologyEdges(mplsDevices?: MplsTopologyQuery) { return links; }); } + +export function convertDiscoveryMplsPathToApiMplsPath(mplsPathQuery: MplsPathQuery): NexusGenObjects['LspPath'] { + const { path: apiPath, lsp_metadata: apiMetadata } = mplsPathQuery.mplsLspPath; + const path = apiPath?.filter(omitNullValue) ?? []; + const metadata = { + fromDevice: apiMetadata?.from_device, + toDevice: apiMetadata?.to_device, + signalization: apiMetadata?.signalisation, + uptime: apiMetadata?.uptime, + }; + return { + path, + metadata, + }; +} diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 6612f859..fe3957e7 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -512,6 +512,18 @@ type LocationEdge { node: Location! } +type LspPath { + metadata: LspPathMetadata + path: [String!]! +} + +type LspPathMetadata { + fromDevice: String + signalization: String + toDevice: String + uptime: Int +} + type LspTunnel { fromDevice: String lspId: String! @@ -758,6 +770,7 @@ type Query { kafkaHealthCheck: IsOkResponse labels(after: String, before: String, filter: FilterLabelsInput, first: Int, last: Int): LabelConnection! locations(after: String, before: String, first: Int, last: Int): LocationConnection! + lspPath(deviceId: String!, lspId: String!): LspPath mplsLspCount(deviceId: String!): MplsLspCount mplsTopology: MplsTopology mplsTopologyVersionData(version: String!): MplsTopologyVersionData! diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 4b7bb728..e7a94a36 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -535,6 +535,18 @@ export interface NexusGenObjects { cursor: string; // String! node: NexusGenRootTypes['Location']; // Location! }; + LspPath: { + // root type + metadata?: NexusGenRootTypes['LspPathMetadata'] | null; // LspPathMetadata + path: string[]; // [String!]! + }; + LspPathMetadata: { + // root type + fromDevice?: string | null; // String + signalization?: string | null; // String + toDevice?: string | null; // String + uptime?: number | null; // Int + }; LspTunnel: { // root type fromDevice?: string | null; // String @@ -1221,6 +1233,18 @@ export interface NexusGenFieldTypes { cursor: string; // String! node: NexusGenRootTypes['Location']; // Location! }; + LspPath: { + // field return type + metadata: NexusGenRootTypes['LspPathMetadata'] | null; // LspPathMetadata + path: string[]; // [String!]! + }; + LspPathMetadata: { + // field return type + fromDevice: string | null; // String + signalization: string | null; // String + toDevice: string | null; // String + uptime: number | null; // Int + }; LspTunnel: { // field return type fromDevice: string | null; // String @@ -1453,6 +1477,7 @@ export interface NexusGenFieldTypes { kafkaHealthCheck: NexusGenRootTypes['IsOkResponse'] | null; // IsOkResponse labels: NexusGenRootTypes['LabelConnection']; // LabelConnection! locations: NexusGenRootTypes['LocationConnection']; // LocationConnection! + lspPath: NexusGenRootTypes['LspPath'] | null; // LspPath mplsLspCount: NexusGenRootTypes['MplsLspCount'] | null; // MplsLspCount mplsTopology: NexusGenRootTypes['MplsTopology'] | null; // MplsTopology mplsTopologyVersionData: NexusGenRootTypes['MplsTopologyVersionData']; // MplsTopologyVersionData! @@ -1998,6 +2023,18 @@ export interface NexusGenFieldTypeNames { cursor: 'String'; node: 'Location'; }; + LspPath: { + // field return type name + metadata: 'LspPathMetadata'; + path: 'String'; + }; + LspPathMetadata: { + // field return type name + fromDevice: 'String'; + signalization: 'String'; + toDevice: 'String'; + uptime: 'Int'; + }; LspTunnel: { // field return type name fromDevice: 'String'; @@ -2230,6 +2267,7 @@ export interface NexusGenFieldTypeNames { kafkaHealthCheck: 'IsOkResponse'; labels: 'LabelConnection'; locations: 'LocationConnection'; + lspPath: 'LspPath'; mplsLspCount: 'MplsLspCount'; mplsTopology: 'MplsTopology'; mplsTopologyVersionData: 'MplsTopologyVersionData'; @@ -2656,6 +2694,11 @@ export interface NexusGenArgTypes { first?: number | null; // Int last?: number | null; // Int }; + lspPath: { + // args + deviceId: string; // String! + lspId: string; // String! + }; mplsLspCount: { // args deviceId: string; // String! diff --git a/src/schema/topology.ts b/src/schema/topology.ts index cb79ee88..6869a182 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -13,6 +13,7 @@ import config from '../config'; import { fromGraphId } from '../helpers/id-helper'; import { convertDeviceMetadataToMapNodes, + convertDiscoveryMplsPathToApiMplsPath, getDeviceInterfaceEdges, getEdgesFromTopologyQuery, getFilterQuery, @@ -1070,3 +1071,39 @@ export const MplsLspCountQuery = queryField('mplsLspCount', { }; }, }); + +export const LspPathMetadata = objectType({ + name: 'LspPathMetadata', + definition: (t) => { + t.string('signalization'); + t.string('fromDevice'); + t.string('toDevice'); + t.int('uptime'); + }, +}); + +export const LspPath = objectType({ + name: 'LspPath', + definition: (t) => { + t.nonNull.field('path', { type: list(nonNull('String')) }); + t.field('metadata', { type: LspPathMetadata }); + }, +}); + +export const LspPathQuery = queryField('lspPath', { + type: 'LspPath', + args: { + deviceId: nonNull(stringArg()), + lspId: nonNull(stringArg()), + }, + resolve: async (_, args, { topologyDiscoveryGraphQLAPI }) => { + const { deviceId, lspId } = args; + const fromNodeNativeId = fromGraphId('GraphNode', deviceId); + + const lspPathResult = await topologyDiscoveryGraphQLAPI?.getLspPath(fromNodeNativeId, lspId); + if (!lspPathResult) { + return null; + } + return convertDiscoveryMplsPathToApiMplsPath(lspPathResult); + }, +}); From 5b42b8dc9804498a2cb0d046ca94d9bedde16018 Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Mon, 2 Sep 2024 12:27:34 +0200 Subject: [PATCH 15/18] FD-696 update apis (#468) * FR-235 start stop time (#453) * stream start/stop activation time added * migration added * add startedAt/stoppedAt to stream * lint fix * update topology and performance monitor apis changes * discovery api changes * type check fix --- codegen-perf-monitoring.yml | 2 +- src/__generated__/perf-monitoring.graphql.ts | 173 ++++++------ .../topology-discovery.graphql.ts | 256 +++++++++--------- src/context.ts | 6 +- .../performance-monitoring-graphql.ts | 120 +++----- .../topology-discovery-graphql.ts | 122 ++++----- src/external-api/topology-discovery.ts | 110 -------- src/external-api/topology-network-types.ts | 2 +- src/helpers/topology.helpers.ts | 136 ++++++---- src/schema/api.graphql | 22 +- src/schema/nexus-typegen.ts | 6 +- src/schema/topology.ts | 28 +- 12 files changed, 424 insertions(+), 559 deletions(-) delete mode 100644 src/external-api/topology-discovery.ts diff --git a/codegen-perf-monitoring.yml b/codegen-perf-monitoring.yml index a41f249d..81d6a033 100644 --- a/codegen-perf-monitoring.yml +++ b/codegen-perf-monitoring.yml @@ -1,5 +1,5 @@ schema: - - 'http://localhost:8082' + - 'http://localhost:8082/api/graphql' documents: - './src/external-api/performance-monitoring-graphql.ts' generates: diff --git a/src/__generated__/perf-monitoring.graphql.ts b/src/__generated__/perf-monitoring.graphql.ts index 37b20ae5..59cbe7bb 100644 --- a/src/__generated__/perf-monitoring.graphql.ts +++ b/src/__generated__/perf-monitoring.graphql.ts @@ -35,125 +35,138 @@ export type BucketWidth = { value: Scalars['Float']; }; -/** Represents the percentage usage value. Includes also device name. */ -export type Percentage = { - __typename?: 'Percentage'; - device: Scalars['String']; - usage: Maybe; -}; - -/** Represents a percentage value at a specific time. */ -export type PercentageInTime = { - __typename?: 'PercentageInTime'; - /** The timestamp indicating when the percentage value was recorded. */ - time: Scalars['Datetime']; - /** The percentage value recorded at the given time. */ - usage: Scalars['Float']; +/** Represents a paginated connection of utilization data for multiple devices. */ +export type BulkUtilizationConnection = { + __typename?: 'BulkUtilizationConnection'; + /** A list of metrics for multiple devices, each with associated cursor information. */ + metrics: Array; + /** Information about the current page of results. */ + pageInfo: PageInfo; }; -/** Represents a series of percentage values over time. Includes also device name. */ -export type PercentageInTimeSeries = { - __typename?: 'PercentageInTimeSeries'; +/** Represents the current sources utilization of a single device. */ +export type CurrentUtilization = { + __typename?: 'CurrentUtilization'; + /** The unique identifier for the device. */ device: Scalars['String']; - usages: Maybe>; + /** The device metrics. */ + deviceMetrics: MetricsNode; }; -export type Query = { - __typename?: 'Query'; - /** Read CPU usages in time range for single device. */ - cpuUsage: PercentageInTimeSeries; - /** Read CPU usages in time range for multiple devices. */ - cpuUsages: Maybe>; - /** Read the current CPU usage for single device. */ - currentCpuUsage: Percentage; - /** Read the current CPU usage for multiple devices. */ - currentCpuUsages: Maybe>; - /** Read the current memory usage for single device. */ - currentMemoryUsage: Percentage; - /** Read the current memory usage for multiple devices. */ - currentMemoryUsages: Maybe>; - /** Read memory usages in time range for single device. */ - memoryUsage: PercentageInTimeSeries; - /** Read memory usages in time range for multiple devices. */ - memoryUsages: Maybe>; -}; - - -export type QueryCpuUsageArgs = { - bucket_width?: InputMaybe; - device: Scalars['String']; - end_time?: InputMaybe; - start_time?: InputMaybe; +/** Represents an edge in a paginated list of device metrics. */ +export type MetricsEdge = { + __typename?: 'MetricsEdge'; + /** A cursor for pagination. */ + cursor: Scalars['String']; + /** A list of device metrics associated with a specific device. */ + deviceMetrics: Array; }; +/** Interface representing device metrics. */ +export type MetricsInterface = { + /** The CPU utilization represented as a percentage. */ + cpu: Maybe; + /** The memory utilization represented as a percentage. */ + memory: Maybe; +}; -export type QueryCpuUsagesArgs = { - bucket_width?: InputMaybe; - devices: Array; - end_time?: InputMaybe; - start_time?: InputMaybe; +/** Base node type for metrics. */ +export type MetricsNode = MetricsInterface & { + __typename?: 'MetricsNode'; + cpu: Maybe; + memory: Maybe; }; +/** Node type for metrics including a cursor. */ +export type MetricsWithCursorNode = MetricsInterface & { + __typename?: 'MetricsWithCursorNode'; + cpu: Maybe; + /** The cursor used for pagination. */ + cursor: Maybe; + memory: Maybe; +}; -export type QueryCurrentCpuUsageArgs = { +/** Node type for metrics a device. */ +export type MetricsWithDeviceNode = MetricsInterface & { + __typename?: 'MetricsWithDeviceNode'; + cpu: Maybe; + /** The device identifier. */ device: Scalars['String']; + memory: Maybe; }; - -export type QueryCurrentCpuUsagesArgs = { - devices: Array; +/** Pagination metadata that is usually coupled to a returned list of objects. */ +export type PageInfo = { + __typename?: 'PageInfo'; + /** Pointer to the last object in the list. */ + endCursor: Maybe; + /** Indicates if there is a next object in the list. */ + hasNextPage: Scalars['Boolean']; }; - -export type QueryCurrentMemoryUsageArgs = { - device: Scalars['String']; +export type Query = { + __typename?: 'Query'; + /** Read current utilization of multiple device sources. */ + bulkCurrentUtilization: Array; + /** Read utilization of multiple device sources. */ + bulkUtilization: BulkUtilizationConnection; + /** Read current utilization of single device sources. */ + currentUtilization: CurrentUtilization; + /** Read utilization of single device device sources. */ + utilization: UtilizationConnection; }; -export type QueryCurrentMemoryUsagesArgs = { +export type QueryBulkCurrentUtilizationArgs = { devices: Array; }; -export type QueryMemoryUsageArgs = { +export type QueryBulkUtilizationArgs = { + after?: InputMaybe; bucket_width?: InputMaybe; - device: Scalars['String']; + devices: Array; end_time?: InputMaybe; + first?: InputMaybe; start_time?: InputMaybe; }; -export type QueryMemoryUsagesArgs = { +export type QueryCurrentUtilizationArgs = { + device: Scalars['String']; +}; + + +export type QueryUtilizationArgs = { + after?: InputMaybe; bucket_width?: InputMaybe; - devices?: InputMaybe>; + device: Scalars['String']; end_time?: InputMaybe; + first?: InputMaybe; start_time?: InputMaybe; }; -export type CurrentMemoryUsagesQueryVariables = Exact<{ - names: Array | Scalars['String']; -}>; - - -export type CurrentMemoryUsagesQuery = { __typename?: 'Query', currentMemoryUsages: Array<{ __typename?: 'Percentage', device: string, usage: number | null }> | null }; - -export type CurrentCpuUsagesQueryVariables = Exact<{ - names: Array | Scalars['String']; -}>; - - -export type CurrentCpuUsagesQuery = { __typename?: 'Query', currentCpuUsages: Array<{ __typename?: 'Percentage', device: string, usage: number | null }> | null }; +/** Represents a paginated connection of utilization data for a single device. */ +export type UtilizationConnection = { + __typename?: 'UtilizationConnection'; + /** The unique identifier for the device. */ + device: Scalars['String']; + /** A list of device metrics with associated cursor information. */ + deviceMetrics: Array; + /** Information about the current page of results. */ + pageInfo: PageInfo; +}; -export type CurrentMemoryUsageQueryVariables = Exact<{ - name: Scalars['String']; +export type BulkDeviceMetricsQueryVariables = Exact<{ + devices: Array | Scalars['String']; }>; -export type CurrentMemoryUsageQuery = { __typename?: 'Query', currentMemoryUsage: { __typename?: 'Percentage', device: string, usage: number | null } }; +export type BulkDeviceMetricsQuery = { __typename?: 'Query', bulkCurrentUtilization: Array<{ __typename?: 'CurrentUtilization', device: string, deviceMetrics: { __typename?: 'MetricsNode', cpu: number | null, memory: number | null } }> }; -export type CurrentCpuUsageQueryVariables = Exact<{ - name: Scalars['String']; +export type DeviceMetricsQueryVariables = Exact<{ + device: Scalars['String']; }>; -export type CurrentCpuUsageQuery = { __typename?: 'Query', currentCpuUsage: { __typename?: 'Percentage', device: string, usage: number | null } }; +export type DeviceMetricsQuery = { __typename?: 'Query', currentUtilization: { __typename?: 'CurrentUtilization', device: string, deviceMetrics: { __typename?: 'MetricsNode', cpu: number | null, memory: number | null } } }; diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index a9cb536e..b2ed66d3 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -17,7 +17,7 @@ export type Scalars = { export type CommonNodesResponse = { __typename?: 'CommonNodesResponse'; /** List of the common node names. Common nodes contain connection to all nodes specified on the input. */ - common_nodes: Array; + commonNodes: Array; }; /** Coordinates of the node on the graph. */ @@ -32,9 +32,9 @@ export type Coordinates = { /** Input of the updateCoordinates mutation that contains information about updated coordinates of a node. */ export type CoordinatesInput = { /** Name of the node in the topology. */ - node_name: Scalars['String']; + nodeName: Scalars['String']; /** Type of the node in the topology. */ - node_type: CoordinatesNodeType; + nodeType: CoordinatesNodeType; /** Updated horizontal coordinate of the node on the graph. */ x: Scalars['Float']; /** Updated vertical coordinate of the node on the graph. */ @@ -44,9 +44,9 @@ export type CoordinatesInput = { /** Type of the node in the topology for which the coordinates are being updated. */ export type CoordinatesNodeType = /** Node that represent device in a topology (for example, PhyDevice or NetDevice collections). */ - | 'device' + | 'DEVICE' /** Node that represents IP network in the network topology (NetNetwork collection). */ - | 'network'; + | 'NETWORK'; /** Response from the updateCoordinates query that contains information about updated coordinated of selected nodes. */ export type CoordinatesResponse = { @@ -54,21 +54,21 @@ export type CoordinatesResponse = { /** Devices that exist in the database. */ installed: InstalledDevices; /** List of node names that do not exist in the database. */ - not_installed: Array; + notInstalled: Array; }; /** Response from the createBackup mutation that contains information about created backup. */ export type CreateBackupResponse = { __typename?: 'CreateBackupResponse'; /** Name of the created backup database. Format: f"backup_{datetime.today().strftime('%Y%m%d%H%M%S')}". */ - db_name: Scalars['String']; + dbName: Scalars['String']; }; /** Response from the deleteBackups mutation that contains information about removed backups. */ export type DeleteBackupsResponse = { __typename?: 'DeleteBackupsResponse'; /** Names of the removed databases that contained backups. */ - deleted_backups: Array; + deletedBackups: Array; }; /** Device GeoLocation data. */ @@ -154,12 +154,12 @@ export type DeviceMetadataFilter = { /** Type of geometry. */ export type GeometryType = - | 'Point'; + | 'POINT'; export type InstalledDevices = { __typename?: 'InstalledDevices'; /** List of node names which coordinates have not been updated. */ - not_updated: Array; + notUpdated: Array; /** List of node names which coordinates have been updated. */ updated: Array; }; @@ -168,13 +168,13 @@ export type InstalledDevices = { export type LspTunnel = { __typename?: 'LspTunnel'; /** From which device is the tunnel originating. */ - from_device: Maybe; + fromDevice: Maybe; /** Name of the link state packet. */ - lsp_id: Scalars['String']; + lspId: Scalars['String']; /** Type of signalisation. */ signalisation: Signalisation; /** Where is the tunnel headed. */ - to_device: Maybe; + toDevice: Maybe; /** Uptime of the tunnel in seconds. */ uptime: Maybe; }; @@ -192,19 +192,19 @@ export type MetadataConnection = { export type MplsData = { __typename?: 'MplsData'; /** The input interface. */ - in_interface: Maybe; + inInterface: Maybe; /** The input label. */ - in_label: Maybe; + inLabel: Maybe; /** Name of the link state packet. */ - lsp_id: Scalars['String']; + lspId: Scalars['String']; /** The operation type. */ - mpls_operation: Maybe; + mplsOperation: Maybe; /** Operational state of the device. */ - oper_state: Maybe; + operState: Maybe; /** The input interface. */ - out_interface: Maybe; + outInterface: Maybe; /** The output label. */ - out_label: Maybe; + outLabel: Maybe; /** Type of signalisation. */ signalisation: Maybe; }; @@ -248,9 +248,9 @@ export type MplsDeviceConnection = { /** Details specific to MPLS (Multi-Protocol Label Switching). */ export type MplsDeviceDetails = { __typename?: 'MplsDeviceDetails'; - lsp_tunnels: Maybe>>; - mpls_data: Maybe>>; - router_id: Maybe; + lspTunnels: Maybe>>; + mplsData: Maybe>>; + routerId: Maybe; }; /** Grouped MplsDevice object and associated cursor used by pagination. */ @@ -341,11 +341,11 @@ export type MplsLinkEdge = { export type MplsLspMetadata = { __typename?: 'MplsLspMetadata'; /** From which device is the tunnel originating. */ - from_device: Scalars['String']; + fromDevice: Scalars['String']; /** Type of signalisation. */ signalisation: Scalars['String']; /** Where is the tunnel headed. */ - to_device: Scalars['String']; + toDevice: Scalars['String']; /** Uptime of the tunnel in seconds. */ uptime: Maybe; }; @@ -353,25 +353,25 @@ export type MplsLspMetadata = { export type MplsLspPath = { __typename?: 'MplsLspPath'; /** LSP metadata. */ - lsp_metadata: Maybe; + lspMetadata: Maybe; /** Ordered list of link IDs & device IDs on the path. */ path: Maybe>>; }; export type MplsOperation = - | 'Noop' - | 'Pop' - | 'Push' - | 'Swap'; + | 'NOOP' + | 'POP' + | 'PUSH' + | 'SWAP'; export type MplsTotalLsps = { __typename?: 'MplsTotalLsps'; /** Number of incoming LSPs. */ - incoming_lsps: Maybe; + incomingLsps: Maybe; /** Number of outcoming LSPs. */ - outcoming_lsps: Maybe; + outcomingLsps: Maybe; /** To which device the LSP is headed. */ - to_device: Maybe; + toDevice: Maybe; }; export type Mutation = { @@ -420,45 +420,45 @@ export type Mutation = { export type MutationDeleteBackupsArgs = { - delete_age?: InputMaybe; + deleteAge?: InputMaybe; }; export type MutationEnableRemoteDebugSessionArgs = { host: Scalars['String']; port?: InputMaybe; - stderr_to_server?: InputMaybe; - stdout_to_server?: InputMaybe; + stderrToServer?: InputMaybe; + stdoutToTerver?: InputMaybe; }; export type MutationSyncArgs = { devices?: InputMaybe>>; labels?: InputMaybe>>; - topology_type: TopologyType; + topologyType: TopologyType; }; export type MutationUpdateCoordinatesArgs = { - coordinates_list: Array; - topology_type?: InputMaybe; + coordinatesList: Array; + topologyType?: InputMaybe; }; export type MutationUpdateNodeStatusArgs = { - device_name: Scalars['String']; - interface_name?: InputMaybe; + deviceName: Scalars['String']; + interfaceName?: InputMaybe; status: NodeStatus; - topology_type?: InputMaybe; + topologyType?: InputMaybe; }; /** Metadata information about a neighbor device. */ export type Neighbor = { __typename?: 'Neighbor'; /** Identifier of the neighbor device document (for example, MplsDevice/1). */ - device_id: Scalars['String']; + deviceId: Scalars['String']; /** Human-readable name of the neighbor device (for example, CPE_01). */ - device_name: Scalars['String']; + deviceName: Scalars['String']; }; /** Representation of the routing entity in the network topology. */ @@ -633,9 +633,9 @@ export type NetRoutingPathConnection = { /** Types of the nodes that should be included in the returned path. */ export type NetRoutingPathOutputCollections = /** Include NetDevice nodes in the returned path. */ - | 'NetDevice' + | 'NET_DEVICE' /** Include NetInterface nodes in the returned path. */ - | 'NetInterface'; + | 'NET_INTERFACE'; /** Generic node that can be identified using Globally Unique ID. */ export type Node = { @@ -655,9 +655,9 @@ export type NodeInfo = { /** Status of the node from the view of the device registry. */ export type NodeStatus = /** Node is known - it has been installed in the device registry. */ - | 'ok' + | 'OK' /** Node is unknown - sync process has detected presence of this node but it is not present in the device registry. */ - | 'unknown'; + | 'UNKNOWN'; /** Pagination metadata that is usually coupled to a returned list of objects. */ export type PageInfo = { @@ -712,9 +712,9 @@ export type PhyDeviceConnection = { export type PhyDeviceDetails = { __typename?: 'PhyDeviceDetails'; /** Device type (e.g. device model, vendor, chassis, hardware details, etc.) */ - device_type: Maybe; + deviceType: Maybe; /** Version of the network operating system running on the device. */ - sw_version: Maybe; + swVersion: Maybe; }; /** Grouped PhyDevice object and associated cursor used by pagination. */ @@ -771,7 +771,7 @@ export type PhyInterfaceConnection = { export type PhyInterfaceDetails = { __typename?: 'PhyInterfaceDetails'; /** Max operational interface bandwidth in Mbit. */ - max_speed: Maybe; + maxSpeed: Maybe; }; /** Grouped PhyInterface object and associated cursor used by pagination. */ @@ -814,7 +814,7 @@ export type PhyLinkEdge = { export type ProviderResponse = { __typename?: 'ProviderResponse'; /** List of the supported device types in the specified topology (e.g. ios, ios xe, sros, etc.) */ - supported_devices: Array; + supportedDevices: Array; }; /** Representation of the device in the ptp topology. */ @@ -861,38 +861,38 @@ export type PtpDeviceDetails = { * by the device based on the characteristics of its internal clock oscillator and how well it can track * the reference time. */ - clock_accuracy: Maybe; + clockAccuracy: Maybe; /** Measure of clock traceability. */ - clock_class: Maybe; + clockClass: Maybe; /** Unique identifier of the clock. */ - clock_id: Maybe; + clockId: Maybe; /** Type of clock (e.g., ordinary, master). */ - clock_type: Maybe; + clockType: Maybe; /** * Measure of clock precision. How much the clock-output varies when not synchronized to another source. * The variance is determined by assessing how much the local clock deviates from the ideal time over a certain period, * often expressed in parts per billion (ppb) or as the standard deviation of the clock's offset. */ - clock_variance: Maybe; + clockVariance: Maybe; /** Domain of the PTP network. */ domain: Maybe; /** Global priority of the clock (the first priority). */ - global_priority: Maybe; + globalPriority: Maybe; /** Unique identifier of the grandmaster clock. */ - gm_clock_id: Maybe; + gmClockId: Maybe; /** Unique identifier of the parent clock. */ - parent_clock_id: Maybe; + parentClockId: Maybe; /** The port state of the device. */ - ptp_port_state: Maybe; + ptpPortState: Maybe; /** PTP profile used (e.g., ITU-T G.8275.1). */ - ptp_profile: Maybe; + ptpProfile: Maybe; /** * Indicates the current state of the time recovery process. Time recovery is the process of adjusting * the local clock to synchronize with a more accurate reference clock. */ - time_recovery_status: Maybe; + timeRecoveryStatus: Maybe; /** User defined value of the second priority. */ - user_priority: Maybe; + userPriority: Maybe; }; /** Grouped PtpDevice object and associated cursor used by pagination. */ @@ -907,15 +907,15 @@ export type PtpDeviceEdge = { /** Filter for PtpDevice type based on device label and device name. */ export type PtpDeviceFilter = { /** Regex: clock accuracy to primary reference. */ - clock_accuracy?: InputMaybe; + clockAccuracy?: InputMaybe; /** Measure of clock traceability. */ - clock_class?: InputMaybe; + clockClass?: InputMaybe; /** Regex: Unique identifier of the clock. */ - clock_id?: InputMaybe; + clockId?: InputMaybe; /** Regex: Type of clock (e.g., ordinary, master). */ - clock_type?: InputMaybe; + clockType?: InputMaybe; /** Regex: measure of clock precision. */ - clock_variance?: InputMaybe; + clockVariance?: InputMaybe; /** Domain of the PTP network. */ domain?: InputMaybe; /** Device label. */ @@ -923,9 +923,9 @@ export type PtpDeviceFilter = { /** Regex of device name. */ name?: InputMaybe; /** PTP profile used (e.g., ITU-T G.8275.1). */ - ptp_profile?: InputMaybe; + ptpProfile?: InputMaybe; /** Regex: indicates the current state of the time recovery process. */ - time_recovery_status?: InputMaybe; + timeRecoveryStatus?: InputMaybe; }; /** A Ptp node that uses a different upstream path in SyncE topology */ @@ -1000,14 +1000,14 @@ export type PtpInterfaceConnection = { export type PtpInterfaceDetails = { __typename?: 'PtpInterfaceDetails'; /** Administrative/operational status of the interface (e.g. 'up/up', 'up/down'). */ - admin_oper_status: Scalars['String']; + adminOperStatus: Scalars['String']; /** State of the PTP process on the interface (e.g. 'master', 'slave', 'disabled', 'passive', 'unknown'). */ - ptp_status: Scalars['String']; + ptpStatus: Scalars['String']; /** * Unusable packet timing signal received by the slave, for example, where the packet delay variation is excessive, * resulting in the slave being unable to meet the output clock performance requirements. */ - ptsf_unusable: Scalars['String']; + ptsfUnusable: Scalars['String']; }; /** Grouped PtpInterface object and associated cursor used by pagination. */ @@ -1022,13 +1022,13 @@ export type PtpInterfaceEdge = { /** Filter for PtpInterface type based on the current interface status and name of the device. */ export type PtpInterfaceFilter = { /** Regex of administrative/operational status on the interface (e.g. 'up/up', 'up/down'). */ - admin_oper_status?: InputMaybe; + adminOperStatus?: InputMaybe; /** Regex of interface name. */ name?: InputMaybe; /** Regex of the PTP process status on the interface. */ - ptp_status?: InputMaybe; + ptpStatus?: InputMaybe; /** Regex of unusable packet timing signal received by the slave. */ - ptsf_unusable?: InputMaybe; + ptsfUnusable?: InputMaybe; /** Status of the interface from the view of the synced topology. */ status?: InputMaybe; }; @@ -1065,9 +1065,9 @@ export type PtpPath = { /** Types of the nodes that should be included in the returned path. */ export type PtpPathOutputCollections = /** Include PtpDevice nodes in the returned path. */ - | 'PtpDevice' + | 'PTP_DEVICE' /** Include PtpInterface nodes in the returned path. */ - | 'PtpInterface'; + | 'PTP_INTERFACE'; export type Query = { __typename?: 'Query'; @@ -1159,9 +1159,9 @@ export type Query = { export type QueryCommonNodesArgs = { - db_name?: InputMaybe; - selected_nodes: Array; - topology_type?: InputMaybe; + dbName?: InputMaybe; + selectedNodes: Array; + topologyType?: InputMaybe; }; @@ -1180,19 +1180,19 @@ export type QueryMplsDevicesArgs = { export type QueryMplsLspCountArgs = { - device_id: Scalars['ID']; + deviceId: Scalars['ID']; }; export type QueryMplsLspPathArgs = { - device_id: Scalars['ID']; - lsp_id: Scalars['ID']; + deviceId: Scalars['ID']; + lspId: Scalars['ID']; }; export type QueryNeighborsArgs = { - device_name: Scalars['String']; - topology_type: TopologyType; + deviceName: Scalars['String']; + topologyType: TopologyType; }; @@ -1223,7 +1223,7 @@ export type QueryPhyDevicesArgs = { export type QueryProviderArgs = { - topology_type: TopologyType; + topologyType: TopologyType; }; @@ -1261,9 +1261,9 @@ export type QuerySyncePathToGmArgs = { export type QueryTopologyDiffArgs = { - collection_type: TopologyType; - new_db: Scalars['String']; - old_db: Scalars['String']; + collectionType: TopologyType; + newDb: Scalars['String']; + oldDb: Scalars['String']; }; @@ -1295,9 +1295,9 @@ export type SyncResponse = { * List of devices that are installed in UniConfig but are missing their metadata in DeviceMetadata collection in the * database. */ - devices_missing_in_inventory: Maybe>>; + devicesMissingInInventory: Maybe>>; /** List of devices that are not installed in UniConfig. */ - devices_missing_in_uniconfig: Maybe>>; + devicesMissingInUniconfig: Maybe>>; /** * List of string labels that are used for grouping of synced devices. * List content should be the same as the list of labels in the input of the sync query. @@ -1328,7 +1328,7 @@ export type SyncResponse = { * ] * } */ - loaded_devices: Scalars['JSON']; + loadedDevices: Scalars['JSON']; }; /** Representation of the device in the synce topology. */ @@ -1371,7 +1371,7 @@ export type SynceDeviceConnection = { export type SynceDeviceDetails = { __typename?: 'SynceDeviceDetails'; /** Identifier of the reference (for example, source interface) that is used to synchronize the clock. */ - selected_for_use: Maybe; + selectedForUse: Maybe; }; /** Grouped SynceDevice object and associated cursor used by pagination. */ @@ -1390,7 +1390,7 @@ export type SynceDeviceFilter = { /** Regex of device name. */ name?: InputMaybe; /** Regex: identifier of the reference (for example, source interface) that is used to synchronize the clock. */ - selected_for_use?: InputMaybe; + selectedForUse?: InputMaybe; }; /** Port attached to the SyncE device. */ @@ -1433,18 +1433,18 @@ export type SynceInterfaceDetails = { * Information about why the interface is not qualified for SyncE synchronization * (set to 'unknown' if the interface is qualified). */ - not_qualified_due_to: Maybe; + notQualifiedDueTo: Maybe; /** * Information about why the interface is not selected for SyncE synchronization * (set to 'unknown' if the interface is selected). */ - not_selected_due_to: Maybe; + notSelectedDueTo: Maybe; /** Statement of whether the interface is qualified for SyncE synchronization. */ - qualified_for_use: Maybe; + qualifiedForUse: Maybe; /** Quality of the received SyncE signal (for example, 'DNU' or 'PRC'). */ - rx_quality_level: Maybe; + rxQualityLevel: Maybe; /** Configured SyncE on the port. */ - synce_enabled: Maybe; + synceEnabled: Maybe; }; /** Grouped SynceInterface object and associated cursor used by pagination. */ @@ -1461,17 +1461,17 @@ export type SynceInterfaceFilter = { /** Regex of interface name. */ name?: InputMaybe; /** Regex: Information about why the interface is not qualified for SyncE synchronization. */ - not_qualified_due_to?: InputMaybe; + notQualifiedDueTo?: InputMaybe; /** Regex: Information about why the interface is not selected for SyncE synchronization. */ - not_selected_due_to?: InputMaybe; + notSelectedDueTo?: InputMaybe; /** Regex: Statement of whether the interface is qualified for SyncE synchronization. */ - qualified_for_use?: InputMaybe; + qualifiedForUse?: InputMaybe; /** Regex: Quality of the received SyncE signal (for example, 'DNU' or 'PRC'). */ - rx_quality_level?: InputMaybe; + rxQualityLevel?: InputMaybe; /** Status of the interface from the view of the synced topology. */ status?: InputMaybe; /** Configured SyncE on the port. */ - synce_enabled?: InputMaybe; + synceEnabled?: InputMaybe; }; /** Grouped list of SynceLinks objects and pagination metadata. */ @@ -1506,9 +1506,9 @@ export type SyncePath = { /** Types of the nodes that should be included in the returned path. */ export type SyncePathOutputCollections = /** Include SynceDevice nodes in the returned path. */ - | 'SynceDevice' + | 'SYNCE_DEVICE' /** Include SynceInterface nodes in the returned path. */ - | 'SynceInterface'; + | 'SYNCE_INTERFACE'; export type TopologyOverlayDevice = { __typename?: 'TopologyOverlayDevice'; @@ -1629,16 +1629,16 @@ export type TopologyResponse = { * "changed": {"PhyDevice": [{"new": {data}, "old"}: {data}], "PhyInterface": [{"new": {data}, "old": {data}], ...} * } */ - diff_data: Maybe; + diffData: Maybe; }; /** Present topology types. */ export type TopologyType = - | 'EthTopology' - | 'MplsTopology' - | 'NetworkTopology' - | 'PhysicalTopology' - | 'PtpTopology'; + | 'ETH_TOPOLOGY' + | 'MPLS_TOPOLOGY' + | 'NETWORK_TOPOLOGY' + | 'PHYSICAL_TOPOLOGY' + | 'PTP_TOPOLOGY'; export type GetShortestPathQueryVariables = Exact<{ deviceFrom: Scalars['ID']; @@ -1652,12 +1652,12 @@ export type GetShortestPathQuery = { __typename?: 'Query', netRoutingPaths: { __ export type TopologyDevicesQueryVariables = Exact<{ [key: string]: never; }>; -export type TopologyDevicesQuery = { __typename?: 'Query', phyDevices: { __typename?: 'PhyDeviceConnection', edges: Array<{ __typename?: 'PhyDeviceEdge', node: { __typename?: 'PhyDevice', id: string, name: string, status: NodeStatus, labels: Array | null, routerId: string | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'PhyDeviceDetails', sw_version: string | null, device_type: string | null }, phyInterfaces: { __typename?: 'PhyInterfaceConnection', edges: Array<{ __typename?: 'PhyInterfaceEdge', node: { __typename?: 'PhyInterface', id: string, name: string, status: NodeStatus, phyLinks: { __typename?: 'PhyLinkConnection', edges: Array<{ __typename?: 'PhyLinkEdge', link: string | null, node: { __typename?: 'PhyInterface', id: string, name: string, phyDevice: { __typename?: 'PhyDevice', id: string, name: string } | null } | null } | null> | null } } | null } | null> | null } } | null } | null> | null } }; +export type TopologyDevicesQuery = { __typename?: 'Query', phyDevices: { __typename?: 'PhyDeviceConnection', edges: Array<{ __typename?: 'PhyDeviceEdge', node: { __typename?: 'PhyDevice', id: string, name: string, status: NodeStatus, labels: Array | null, routerId: string | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'PhyDeviceDetails', swVersion: string | null, deviceType: string | null }, phyInterfaces: { __typename?: 'PhyInterfaceConnection', edges: Array<{ __typename?: 'PhyInterfaceEdge', node: { __typename?: 'PhyInterface', id: string, name: string, status: NodeStatus, phyLinks: { __typename?: 'PhyLinkConnection', edges: Array<{ __typename?: 'PhyLinkEdge', link: string | null, node: { __typename?: 'PhyInterface', id: string, name: string, phyDevice: { __typename?: 'PhyDevice', id: string, name: string } | null } | null } | null> | null } } | null } | null> | null } } | null } | null> | null } }; export type NetTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type NetTopologyQuery = { __typename?: 'Query', netDevices: { __typename?: 'NetDeviceConnection', edges: Array<{ __typename?: 'NetDeviceEdge', cursor: string, node: { __typename?: 'NetDevice', id: string, routerId: string, ospfAreaId: string, phyDevice: { __typename?: 'PhyDevice', id: string, name: string, status: NodeStatus, labels: Array | null, routerId: string | null, details: { __typename?: 'PhyDeviceDetails', device_type: string | null, sw_version: string | null }, coordinates: { __typename?: 'Coordinates', x: number, y: number } } | null, netInterfaces: { __typename?: 'NetInterfaceConnection', edges: Array<{ __typename?: 'NetInterfaceEdge', cursor: string, node: { __typename?: 'NetInterface', id: string, ipAddress: string, netDevice: { __typename?: 'NetDevice', id: string, routerId: string } | null, netLinks: { __typename?: 'NetLinkConnection', edges: Array<{ __typename?: 'NetLinkEdge', link: string | null, node: { __typename?: 'NetInterface', id: string, igp_metric: number | null, netDevice: { __typename?: 'NetDevice', id: string, routerId: string } | null } | null } | null> | null } } | null } | null> | null }, netNetworks: { __typename?: 'NetNetworkConnection', edges: Array<{ __typename?: 'NetNetworkEdge', cursor: string, node: { __typename?: 'NetNetwork', id: string, subnet: string, ospfRouteType: number, coordinates: { __typename?: 'Coordinates', x: number, y: number } } | null } | null> | null, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor: string | null } | null } } | null } | null> | null } }; +export type NetTopologyQuery = { __typename?: 'Query', netDevices: { __typename?: 'NetDeviceConnection', edges: Array<{ __typename?: 'NetDeviceEdge', cursor: string, node: { __typename?: 'NetDevice', id: string, routerId: string, ospfAreaId: string, phyDevice: { __typename?: 'PhyDevice', id: string, name: string, status: NodeStatus, labels: Array | null, routerId: string | null, details: { __typename?: 'PhyDeviceDetails', deviceType: string | null, swVersion: string | null }, coordinates: { __typename?: 'Coordinates', x: number, y: number } } | null, netInterfaces: { __typename?: 'NetInterfaceConnection', edges: Array<{ __typename?: 'NetInterfaceEdge', cursor: string, node: { __typename?: 'NetInterface', id: string, ipAddress: string, netDevice: { __typename?: 'NetDevice', id: string, routerId: string } | null, netLinks: { __typename?: 'NetLinkConnection', edges: Array<{ __typename?: 'NetLinkEdge', link: string | null, node: { __typename?: 'NetInterface', id: string, igp_metric: number | null, netDevice: { __typename?: 'NetDevice', id: string, routerId: string } | null } | null } | null> | null } } | null } | null> | null }, netNetworks: { __typename?: 'NetNetworkConnection', edges: Array<{ __typename?: 'NetNetworkEdge', cursor: string, node: { __typename?: 'NetNetwork', id: string, subnet: string, ospfRouteType: number, coordinates: { __typename?: 'Coordinates', x: number, y: number } } | null } | null> | null, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor: string | null } | null } } | null } | null> | null } }; export type GetBackupsQueryVariables = Exact<{ [key: string]: never; }>; @@ -1671,7 +1671,7 @@ export type TopologyDiffQueryVariables = Exact<{ }>; -export type TopologyDiffQuery = { __typename?: 'Query', topologyDiff: { __typename?: 'TopologyResponse', diff_data: any | null } }; +export type TopologyDiffQuery = { __typename?: 'Query', topologyDiff: { __typename?: 'TopologyResponse', diffData: any | null } }; export type PtpDiffSynceQueryVariables = Exact<{ [key: string]: never; }>; @@ -1683,7 +1683,7 @@ export type GetCommonNodesQueryVariables = Exact<{ }>; -export type GetCommonNodesQuery = { __typename?: 'Query', commonNodes: { __typename?: 'CommonNodesResponse', common_nodes: Array } }; +export type GetCommonNodesQuery = { __typename?: 'Query', commonNodes: { __typename?: 'CommonNodesResponse', commonNodes: Array } }; export type UpdateCoordinatesMutationVariables = Exact<{ coordinates: Array | CoordinatesInput; @@ -1691,9 +1691,9 @@ export type UpdateCoordinatesMutationVariables = Exact<{ }>; -export type UpdateCoordinatesMutation = { __typename?: 'Mutation', updateCoordinates: { __typename?: 'CoordinatesResponse', not_installed: Array, installed: { __typename?: 'InstalledDevices', not_updated: Array, updated: Array } } }; +export type UpdateCoordinatesMutation = { __typename?: 'Mutation', updateCoordinates: { __typename?: 'CoordinatesResponse', notInstalled: Array, installed: { __typename?: 'InstalledDevices', notUpdated: Array, updated: Array } } }; -export type PtpDevicePartsFragment = { __typename?: 'PtpDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'PtpDeviceDetails', clock_type: string | null, domain: number | null, ptp_profile: string | null, clock_id: string | null, parent_clock_id: string | null, gm_clock_id: string | null, clock_class: number | null, clock_accuracy: string | null, clock_variance: string | null, time_recovery_status: string | null, global_priority: number | null, user_priority: number | null }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', cursor: string, node: { __typename?: 'PtpInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'PtpInterfaceDetails', ptp_status: string, ptsf_unusable: string, admin_oper_status: string } | null, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, ptpDevice: { __typename?: 'PtpDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', node: { __typename?: 'PtpInterface', id: string, name: string, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, name: string } | null } | null> | null } } | null } | null> | null } } | null } | null } | null> | null } } | null } | null> | null } }; +export type PtpDevicePartsFragment = { __typename?: 'PtpDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'PtpDeviceDetails', clockType: string | null, domain: number | null, ptpProfile: string | null, clockId: string | null, parentClockId: string | null, gmClockId: string | null, clockClass: number | null, clockAccuracy: string | null, clockVariance: string | null, timeRecoveryStatus: string | null, globalPriority: number | null, userPriority: number | null }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', cursor: string, node: { __typename?: 'PtpInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'PtpInterfaceDetails', ptpStatus: string, ptsfUnusable: string, adminOperStatus: string } | null, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, ptpDevice: { __typename?: 'PtpDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', node: { __typename?: 'PtpInterface', id: string, name: string, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, name: string } | null } | null> | null } } | null } | null> | null } } | null } | null } | null> | null } } | null } | null> | null } }; export type PtpInterfaceDevicePartsFragment = { __typename?: 'PtpDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', node: { __typename?: 'PtpInterface', id: string, name: string, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, name: string } | null } | null> | null } } | null } | null> | null } }; @@ -1702,7 +1702,7 @@ export type PtpInterfacePartsFragment = { __typename?: 'PtpInterface', id: strin export type PtpTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type PtpTopologyQuery = { __typename?: 'Query', ptpDevices: { __typename?: 'PtpDeviceConnection', edges: Array<{ __typename?: 'PtpDeviceEdge', cursor: string, node: { __typename?: 'PtpDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'PtpDeviceDetails', clock_type: string | null, domain: number | null, ptp_profile: string | null, clock_id: string | null, parent_clock_id: string | null, gm_clock_id: string | null, clock_class: number | null, clock_accuracy: string | null, clock_variance: string | null, time_recovery_status: string | null, global_priority: number | null, user_priority: number | null }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', cursor: string, node: { __typename?: 'PtpInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'PtpInterfaceDetails', ptp_status: string, ptsf_unusable: string, admin_oper_status: string } | null, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, ptpDevice: { __typename?: 'PtpDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', node: { __typename?: 'PtpInterface', id: string, name: string, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, name: string } | null } | null> | null } } | null } | null> | null } } | null } | null } | null> | null } } | null } | null> | null } } | null } | null> | null } }; +export type PtpTopologyQuery = { __typename?: 'Query', ptpDevices: { __typename?: 'PtpDeviceConnection', edges: Array<{ __typename?: 'PtpDeviceEdge', cursor: string, node: { __typename?: 'PtpDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'PtpDeviceDetails', clockType: string | null, domain: number | null, ptpProfile: string | null, clockId: string | null, parentClockId: string | null, gmClockId: string | null, clockClass: number | null, clockAccuracy: string | null, clockVariance: string | null, timeRecoveryStatus: string | null, globalPriority: number | null, userPriority: number | null }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', cursor: string, node: { __typename?: 'PtpInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'PtpInterfaceDetails', ptpStatus: string, ptsfUnusable: string, adminOperStatus: string } | null, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, ptpDevice: { __typename?: 'PtpDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, ptpInterfaces: { __typename?: 'PtpInterfaceConnection', edges: Array<{ __typename?: 'PtpInterfaceEdge', node: { __typename?: 'PtpInterface', id: string, name: string, ptpLinks: { __typename?: 'PtpLinkConnection', edges: Array<{ __typename?: 'PtpLinkEdge', link: string | null, node: { __typename?: 'PtpInterface', id: string, name: string } | null } | null> | null } } | null } | null> | null } } | null } | null } | null> | null } } | null } | null> | null } } | null } | null> | null } }; export type PtpPathToGrandMasterQueryVariables = Exact<{ deviceFrom: Scalars['ID']; @@ -1711,7 +1711,7 @@ export type PtpPathToGrandMasterQueryVariables = Exact<{ export type PtpPathToGrandMasterQuery = { __typename?: 'Query', ptpPathToGmClock: { __typename?: 'PtpPath', nodes: Array | null } }; -export type SynceDevicePartsFragment = { __typename?: 'SynceDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'SynceDeviceDetails', selected_for_use: string | null }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', cursor: string, node: { __typename?: 'SynceInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'SynceInterfaceDetails', synce_enabled: boolean | null, rx_quality_level: string | null, qualified_for_use: string | null, not_qualified_due_to: string | null, not_selected_due_to: string | null } | null, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null } }; +export type SynceDevicePartsFragment = { __typename?: 'SynceDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'SynceDeviceDetails', selectedForUse: string | null }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', cursor: string, node: { __typename?: 'SynceInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'SynceInterfaceDetails', synceEnabled: boolean | null, rxQualityLevel: string | null, qualifiedForUse: string | null, notQualifiedDueTo: string | null, notSelectedDueTo: string | null } | null, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null } }; export type SynceInterfaceDevicePartsFragment = { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } }; @@ -1720,7 +1720,7 @@ export type SynceInterfacePartsFragment = { __typename?: 'SynceInterface', id: s export type SynceTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type SynceTopologyQuery = { __typename?: 'Query', synceDevices: { __typename?: 'SynceDeviceConnection', edges: Array<{ __typename?: 'SynceDeviceEdge', cursor: string, node: { __typename?: 'SynceDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'SynceDeviceDetails', selected_for_use: string | null }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', cursor: string, node: { __typename?: 'SynceInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'SynceInterfaceDetails', synce_enabled: boolean | null, rx_quality_level: string | null, qualified_for_use: string | null, not_qualified_due_to: string | null, not_selected_due_to: string | null } | null, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null } } | null } | null> | null } }; +export type SynceTopologyQuery = { __typename?: 'Query', synceDevices: { __typename?: 'SynceDeviceConnection', edges: Array<{ __typename?: 'SynceDeviceEdge', cursor: string, node: { __typename?: 'SynceDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, details: { __typename?: 'SynceDeviceDetails', selectedForUse: string | null }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', cursor: string, node: { __typename?: 'SynceInterface', id: string, name: string, status: NodeStatus, details: { __typename?: 'SynceInterfaceDetails', synceEnabled: boolean | null, rxQualityLevel: string | null, qualifiedForUse: string | null, notQualifiedDueTo: string | null, notSelectedDueTo: string | null } | null, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, synceDevice: { __typename?: 'SynceDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, synceInterfaces: { __typename?: 'SynceInterfaceConnection', edges: Array<{ __typename?: 'SynceInterfaceEdge', node: { __typename?: 'SynceInterface', id: string, name: string, synceLinks: { __typename?: 'SynceLinkConnection', edges: Array<{ __typename?: 'SynceLinkEdge', link: string | null, node: { __typename?: 'SynceInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null } } | null } | null> | null } }; export type SyncePathToGrandMasterQueryVariables = Exact<{ deviceFrom: Scalars['ID']; @@ -1736,7 +1736,7 @@ export type DeviceMetadataQueryVariables = Exact<{ export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; -export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; +export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', routerId: string | null, mplsData: Array<{ __typename?: 'MplsData', lspId: string, inLabel: number | null, inInterface: string | null, outInterface: string | null, outLabel: number | null, mplsOperation: MplsOperation | null, operState: string | null, signalisation: Signalisation | null } | null> | null, lspTunnels: Array<{ __typename?: 'LspTunnel', lspId: string, fromDevice: string | null, toDevice: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; export type MplsInterfaceDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } }; @@ -1745,14 +1745,14 @@ export type MplsInterfacePartsFragment = { __typename?: 'MplsInterface', id: str export type MplsTopologyQueryVariables = Exact<{ [key: string]: never; }>; -export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', router_id: string | null, mpls_data: Array<{ __typename?: 'MplsData', lsp_id: string, in_label: number | null, in_interface: string | null, out_interface: string | null, out_label: number | null, mpls_operation: MplsOperation | null, oper_state: string | null, signalisation: Signalisation | null } | null> | null, lsp_tunnels: Array<{ __typename?: 'LspTunnel', lsp_id: string, from_device: string | null, to_device: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; +export type MplsTopologyQuery = { __typename?: 'Query', mplsDevices: { __typename?: 'MplsDeviceConnection', edges: Array<{ __typename?: 'MplsDeviceEdge', cursor: string, node: { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', routerId: string | null, mplsData: Array<{ __typename?: 'MplsData', lspId: string, inLabel: number | null, inInterface: string | null, outInterface: string | null, outLabel: number | null, mplsOperation: MplsOperation | null, operState: string | null, signalisation: Signalisation | null } | null> | null, lspTunnels: Array<{ __typename?: 'LspTunnel', lspId: string, fromDevice: string | null, toDevice: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } } | null } | null> | null } }; export type MplsLspCountQueryVariables = Exact<{ deviceId: Scalars['ID']; }>; -export type MplsLspCountQuery = { __typename?: 'Query', mplsLspCount: Array<{ __typename?: 'MplsTotalLsps', to_device: string | null, incoming_lsps: number | null, outcoming_lsps: number | null } | null> | null }; +export type MplsLspCountQuery = { __typename?: 'Query', mplsLspCount: Array<{ __typename?: 'MplsTotalLsps', toDevice: string | null, incomingLsps: number | null, outcomingLsps: number | null } | null> | null }; export type MplsPathQueryVariables = Exact<{ deviceId: Scalars['ID']; @@ -1760,7 +1760,7 @@ export type MplsPathQueryVariables = Exact<{ }>; -export type MplsPathQuery = { __typename?: 'Query', mplsLspPath: { __typename?: 'MplsLspPath', path: Array | null, lsp_metadata: { __typename?: 'MplsLspMetadata', from_device: string, to_device: string, uptime: number | null, signalisation: string } | null } }; +export type MplsPathQuery = { __typename?: 'Query', mplsLspPath: { __typename?: 'MplsLspPath', path: Array | null, lspMetadata: { __typename?: 'MplsLspMetadata', fromDevice: string, toDevice: string, uptime: number | null, signalisation: string } | null } }; export type NeighborsQueryVariables = Exact<{ deviceName: Scalars['String']; @@ -1768,4 +1768,4 @@ export type NeighborsQueryVariables = Exact<{ }>; -export type NeighborsQuery = { __typename?: 'Query', neighbors: Array<{ __typename?: 'Neighbor', device_id: string, device_name: string }> | null }; +export type NeighborsQuery = { __typename?: 'Query', neighbors: Array<{ __typename?: 'Neighbor', deviceId: string, deviceName: string }> | null }; diff --git a/src/context.ts b/src/context.ts index 6f2237e4..8356d583 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,7 +1,7 @@ import { ExpressContextFunctionArgument } from '@apollo/server/express4'; import { PrismaClient } from '@prisma/client'; import { IncomingHttpHeaders } from 'http'; -import topologyDiscoveryAPI, { TopologyDiscoveryAPI } from './external-api/topology-discovery'; +// import topologyDiscoveryAPI, { TopologyDiscoveryAPI } from './external-api/topology-discovery'; import getTopologyDiscoveryApi, { TopologyDiscoveryGraphQLAPI } from './external-api/topology-discovery-graphql'; import uniconfigAPI, { UniConfigAPI } from './external-api/uniconfig'; import prismaClient from './prisma-client'; @@ -14,7 +14,7 @@ export type Context = { prisma: PrismaClient; tenantId: string; uniconfigAPI: UniConfigAPI; - topologyDiscoveryAPI: TopologyDiscoveryAPI; + // topologyDiscoveryAPI: TopologyDiscoveryAPI; topologyDiscoveryGraphQLAPI?: TopologyDiscoveryGraphQLAPI; performanceMonitoringAPI: PerformanceMonitoringAPI; inventoryKafka: typeof kafkaProducers | null; @@ -44,7 +44,7 @@ export default async function createContext(context: ExpressContextFunctionArgum prisma: prismaClient, tenantId, uniconfigAPI, - topologyDiscoveryAPI, + // topologyDiscoveryAPI, topologyDiscoveryGraphQLAPI: getTopologyDiscoveryApi(), performanceMonitoringAPI: getPerformanceMonitoringAPI(), ...(config.kafkaEnabled ? { kafka, inventoryKafka: kafkaProducers } : { kafka: null, inventoryKafka: null }), diff --git a/src/external-api/performance-monitoring-graphql.ts b/src/external-api/performance-monitoring-graphql.ts index f48cbecb..b5da5735 100644 --- a/src/external-api/performance-monitoring-graphql.ts +++ b/src/external-api/performance-monitoring-graphql.ts @@ -1,14 +1,10 @@ import { gql, GraphQLClient } from 'graphql-request'; import config from '../config'; import { - CurrentCpuUsageQuery, - CurrentCpuUsageQueryVariables, - CurrentCpuUsagesQuery, - CurrentCpuUsagesQueryVariables, - CurrentMemoryUsageQuery, - CurrentMemoryUsageQueryVariables, - CurrentMemoryUsagesQuery, - CurrentMemoryUsagesQueryVariables, + BulkDeviceMetricsQuery, + BulkDeviceMetricsQueryVariables, + DeviceMetricsQuery, + DeviceMetricsQueryVariables, } from '../__generated__/perf-monitoring.graphql'; export type DeviceLoadUsage = { @@ -20,38 +16,26 @@ export type NodesConnectionStatus = { status: 'complete' | 'fail'; }; -const DEVICE_MEMORY_USAGES = gql` - query CurrentMemoryUsages($names: [String!]!) { - currentMemoryUsages(devices: $names) { +const BULK_DEVICE_METRICS = gql` + query BulkDeviceMetrics($devices: [String!]!) { + bulkCurrentUtilization(devices: $devices) { device - usage - } - } -`; - -const DEVICE_CPU_USAGES = gql` - query CurrentCpuUsages($names: [String!]!) { - currentCpuUsages(devices: $names) { - device - usage - } - } -`; - -const DEVICE_MEMORY_USAGE = gql` - query CurrentMemoryUsage($name: String!) { - currentMemoryUsage(device: $name) { - device - usage + deviceMetrics { + cpu + memory + } } } `; -const DEVICE_CPU_USAGE = gql` - query CurrentCpuUsage($name: String!) { - currentCpuUsage(device: $name) { +const DEVICE_METRICS = gql` + query DeviceMetrics($device: String!) { + currentUtilization(device: $device) { device - usage + deviceMetrics { + cpu + memory + } } } `; @@ -67,75 +51,35 @@ function getPerformanceMonitoringAPI() { } const client = new GraphQLClient(config.performanceMonitoringGraphqlURL); - async function getDeviceCpuUsage(deviceName: string): Promise { - const result = await client.request(DEVICE_CPU_USAGE, { - name: deviceName, + async function getDeviceMetrics(deviceName: string): Promise { + const result = await client.request(DEVICE_METRICS, { + device: deviceName, }); return result; } - async function getDeviceMemoryUsage(deviceName: string): Promise { - const result = await client.request( - DEVICE_MEMORY_USAGE, - { name: deviceName }, - ); - - return result; - } - - async function getDeviceCpuUsages(deviceNames: string[]): Promise { - const result = await client.request(DEVICE_CPU_USAGES, { - names: deviceNames, + async function getBulkDeviceMetrics(deviceNames: string[]): Promise { + const result = await client.request(BULK_DEVICE_METRICS, { + devices: deviceNames, }); return result; } - async function getDeviceMemoryUsages(deviceNames: string[]): Promise { - const result = await client.request( - DEVICE_MEMORY_USAGES, - { names: deviceNames }, - ); - - return result; - } - async function getDeviceLoadUsage(deviceName: string): Promise { - const [cpuUsage, memoryUsage] = await Promise.all([ - getDeviceCpuUsage(deviceName), - getDeviceMemoryUsage(deviceName), - ]); + const { currentUtilization } = await getDeviceMetrics(deviceName); - return { cpuUsage: cpuUsage.currentCpuUsage.usage, memoryUsage: memoryUsage.currentMemoryUsage.usage }; + return { cpuUsage: currentUtilization.deviceMetrics.cpu, memoryUsage: currentUtilization.deviceMetrics.memory }; } async function getDeviceLoadUsages(deviceNames: string[]): Promise<(DeviceLoadUsage & { deviceName: string })[]> { - const [cpuUsages, memoryUsages] = await Promise.all([ - getDeviceCpuUsages(deviceNames), - getDeviceMemoryUsages(deviceNames), - ]); - - const map = new Map(); - - cpuUsages.currentCpuUsages?.forEach((cpuUsage) => { - map.set(cpuUsage.device, { deviceName: cpuUsage.device, cpuUsage: cpuUsage.usage, memoryUsage: null }); - }); - - memoryUsages.currentMemoryUsages?.forEach((memoryUsage) => { - const deviceLoadUsage = map.get(memoryUsage.device); - if (deviceLoadUsage) { - deviceLoadUsage.memoryUsage = memoryUsage.usage; - } else { - map.set(memoryUsage.device, { - deviceName: memoryUsage.device, - cpuUsage: null, - memoryUsage: memoryUsage.usage, - }); - } - }); - - return Array.from(map.values()); + const { bulkCurrentUtilization } = await getBulkDeviceMetrics(deviceNames); + return bulkCurrentUtilization.map((u) => ({ + deviceName: u.device, + cpuUsage: u.deviceMetrics.cpu, + memoryUsage: u.deviceMetrics.memory, + })); } return { getDeviceLoadUsage, getDeviceLoadUsages }; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index 56dc70f8..cf2d0c40 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -41,7 +41,7 @@ type CoordinatesParam = { type DeviceMetadataFilters = { deviceName?: string | null; - topologyType?: 'PhysicalTopology' | 'PtpTopology' | 'EthTopology' | 'NetworkTopology' | 'MplsTopology' | null; + topologyType?: 'PHYSICAL_TOPOLOGY' | 'PTP_TOPOLOGY' | 'ETH_TOPOLOGY' | 'NETWORK_TOPOLOGY' | 'MPLS_TOPOLOGY' | null; polygon?: number[][][] | null; }; @@ -74,8 +74,8 @@ const GET_TOPOLOGY_DEVICES = gql` y } details { - sw_version - device_type + swVersion + deviceType } phyInterfaces { edges { @@ -119,8 +119,8 @@ const GET_NET_TOPOLOGY_DEVICES = gql` name status details { - device_type - sw_version + deviceType + swVersion } labels routerId @@ -187,8 +187,8 @@ const GET_BACKUPS = gql` const GET_TOPOLOGY_DIFF = gql` query topologyDiff($new_db: String!, $old_db: String!, $collection_type: TopologyType!) { - topologyDiff(new_db: $new_db, old_db: $old_db, collection_type: $collection_type) { - diff_data + topologyDiff(newDb: $new_db, oldDb: $old_db, collectionType: $collection_type) { + diffData } } `; @@ -207,18 +207,18 @@ const GET_PTP_DIFF_SYNCE = gql` const GET_COMMON_NODES = gql` query getCommonNodes($selectedNodes: [String!]!) { - commonNodes(selected_nodes: $selectedNodes) { - common_nodes + commonNodes(selectedNodes: $selectedNodes) { + commonNodes } } `; const UPDATE_COORDINATES = gql` mutation UpdateCoordinates($coordinates: [CoordinatesInput!]!, $topology_type: TopologyType) { - updateCoordinates(coordinates_list: $coordinates, topology_type: $topology_type) { - not_installed + updateCoordinates(coordinatesList: $coordinates, topologyType: $topology_type) { + notInstalled installed { - not_updated + notUpdated updated } } @@ -234,18 +234,18 @@ const PTP_TOPOLOGY = gql` y } details { - clock_type + clockType domain - ptp_profile - clock_id - parent_clock_id - gm_clock_id - clock_class - clock_accuracy - clock_variance - time_recovery_status - global_priority - user_priority + ptpProfile + clockId + parentClockId + gmClockId + clockClass + clockAccuracy + clockVariance + timeRecoveryStatus + globalPriority + userPriority } status labels @@ -255,9 +255,9 @@ const PTP_TOPOLOGY = gql` node { ...PtpInterfaceParts details { - ptp_status - ptsf_unusable - admin_oper_status + ptpStatus + ptsfUnusable + adminOperStatus } } } @@ -336,7 +336,7 @@ const SYNCE_TOPOLOGY = gql` y } details { - selected_for_use + selectedForUse } status labels @@ -346,11 +346,11 @@ const SYNCE_TOPOLOGY = gql` node { ...SynceInterfaceParts details { - synce_enabled - rx_quality_level - qualified_for_use - not_qualified_due_to - not_selected_due_to + synceEnabled + rxQualityLevel + qualifiedForUse + notQualifiedDueTo + notSelectedDueTo } } } @@ -464,21 +464,21 @@ const MPLS_TOPOLOGY = gql` } } details { - router_id - mpls_data { - lsp_id - in_label - in_interface - out_interface - out_label - mpls_operation - oper_state + routerId + mplsData { + lspId + inLabel + inInterface + outInterface + outLabel + mplsOperation + operState signalisation } - lsp_tunnels { - lsp_id - from_device - to_device + lspTunnels { + lspId + fromDevice + toDevice signalisation uptime } @@ -545,21 +545,21 @@ const MPLS_TOPOLOGY = gql` const MPLS_LSP_COUNT = gql` query MplsLspCount($deviceId: ID!) { - mplsLspCount(device_id: $deviceId) { - to_device - incoming_lsps - outcoming_lsps + mplsLspCount(deviceId: $deviceId) { + toDevice + incomingLsps + outcomingLsps } } `; const MPLS_LSP_PATH = gql` query MplsPath($deviceId: ID!, $lspId: ID!) { - mplsLspPath(device_id: $deviceId, lsp_id: $lspId) { + mplsLspPath(deviceId: $deviceId, lspId: $lspId) { path - lsp_metadata { - from_device - to_device + lspMetadata { + fromDevice + toDevice uptime signalisation } @@ -569,9 +569,9 @@ const MPLS_LSP_PATH = gql` const MAP_NEIGHBORS = gql` query Neighbors($deviceName: String!, $topologyType: TopologyType!) { - neighbors(device_name: $deviceName, topology_type: $topologyType) { - device_id - device_name + neighbors(deviceName: $deviceName, topologyType: $topologyType) { + deviceId + deviceName } } `; @@ -587,7 +587,7 @@ function getTopologyDiscoveryApi() { const response = await client.request(GET_SHORTEST_PATH, { deviceFrom: from, deviceTo: to, - collection: 'NetInterface', + collection: 'NET_INTERFACE', }); return response; @@ -624,7 +624,7 @@ function getTopologyDiscoveryApi() { // eslint-disable-next-line @typescript-eslint/naming-convention collection_type: collectionType, }); - const json = decodeTopologyDiffOutput(response.topologyDiff.diff_data); + const json = decodeTopologyDiffOutput(response.topologyDiff.diffData); return json; } @@ -634,15 +634,15 @@ function getTopologyDiscoveryApi() { selectedNodes, }); - return response.commonNodes.common_nodes; + return response.commonNodes.commonNodes; } async function updateCoordinates(coordinates: CoordinatesParam[], topologyType?: TopologyType): Promise { const coordinatesInput: CoordinatesInput[] = coordinates.map((c) => ({ // eslint-disable-next-line @typescript-eslint/naming-convention - node_name: c.device, + nodeName: c.device, // eslint-disable-next-line @typescript-eslint/naming-convention - node_type: 'device', + nodeType: 'DEVICE', x: c.x, y: c.y, })); diff --git a/src/external-api/topology-discovery.ts b/src/external-api/topology-discovery.ts deleted file mode 100644 index 1c11ed0b..00000000 --- a/src/external-api/topology-discovery.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { sendGetRequest, sendPatchRequest, sendPostRequest } from './helpers'; -import { - decodeHasAndInterfacesOutput, - decodeLinksAndDevicesOutput, - decodeNetAdvertisesAndNetworks, - decodeNetHasAndInterfacesOutput, - decodeNetLinksAndDevicesOutput, - decodeTopologyCommonNodesOutput, - decodeTopologyDiffOutput, - decodeUpdateCoordinatesOutput, - decodeVersionsOutput, - HasAndInterfacesOutput, - LinksAndDevicesOutput, - NetAdvertisesAndNetworksOutput, - NetHasAndInterfacesOutput, - NetLinksAndDevicesOutput, - TopologyCommonNodesOutput, - TopologyDiffOutput, - UpdateCoordinatesOutput, - VersionsOutput, -} from './topology-network-types'; - -async function getVersions(baseURL: string): Promise { - const json = await sendGetRequest([baseURL, '/backup']); - const data = decodeVersionsOutput(json); - return data; -} - -async function getTopologyDiff(baseURL: string, version: string): Promise { - const body = { - // eslint-disable-next-line @typescript-eslint/naming-convention - new_db: 'current', - // eslint-disable-next-line @typescript-eslint/naming-convention - old_db: version, - }; - const json = await sendPostRequest([baseURL, '/diff'], body); - const data = decodeTopologyDiffOutput(json); - return data; -} - -async function getCommonNodes(baseURL: string, nodes: string[]): Promise { - const body = { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'selected-nodes': nodes, - }; - const json = await sendPostRequest([baseURL, '/common-nodes'], body); - const data = decodeTopologyCommonNodesOutput(json); - return data; -} - -async function getLinksAndDevices(baseURL: string): Promise { - const json = await sendGetRequest([baseURL, '/links-and-devices']); - const data = decodeLinksAndDevicesOutput(json); - return data; -} - -async function getHasAndInterfaces(baseURL: string): Promise { - const json = await sendGetRequest([baseURL, '/has-and-interfaces']); - const data = decodeHasAndInterfacesOutput(json); - return data; -} - -type NodeCoordinatesBody = { - device: string; - x: number; - y: number; -}; - -async function updateCoordinates( - baseURL: string, - nodeCoordinates: NodeCoordinatesBody[], -): Promise { - const json = await sendPatchRequest([baseURL, '/coordinates'], nodeCoordinates); - const data = decodeUpdateCoordinatesOutput(json); - return data; -} - -async function getNetHasAndInterfaces(baseURL: string): Promise { - const json = await sendGetRequest([baseURL, '/net-has-and-interfaces']); - const data = decodeNetHasAndInterfacesOutput(json); - return data; -} - -async function getNetLinksAndDevices(baseURL: string): Promise { - const json = await sendGetRequest([baseURL, '/net-links-and-devices']); - const data = decodeNetLinksAndDevicesOutput(json); - return data; -} - -async function getNetAdvertisesAndNetworks(baseURL: string): Promise { - const json = await sendGetRequest([baseURL, '/net-advertises-and-networks']); - const data = decodeNetAdvertisesAndNetworks(json); - return data; -} - -const topologyDiscoveryAPI = { - getVersions, - getTopologyDiff, - getCommonNodes, - getLinksAndDevices, - getHasAndInterfaces, - updateCoordinates, - getNetHasAndInterfaces, - getNetLinksAndDevices, - getNetAdvertisesAndNetworks, -}; - -export type TopologyDiscoveryAPI = typeof topologyDiscoveryAPI; - -export default topologyDiscoveryAPI; diff --git a/src/external-api/topology-network-types.ts b/src/external-api/topology-network-types.ts index 33802c83..16f80c24 100644 --- a/src/external-api/topology-network-types.ts +++ b/src/external-api/topology-network-types.ts @@ -54,7 +54,7 @@ const NetInterface = t.type({ ip_address: t.string, }); export type NetInterface = t.TypeOf; -const StatusValidator = t.union([t.literal('ok'), t.literal('unknown')]); +const StatusValidator = t.union([t.literal('OK'), t.literal('UNKNOWN')]); const InterfaceWithStatusValidator = t.intersection([ Interface, t.type({ diff --git a/src/helpers/topology.helpers.ts b/src/helpers/topology.helpers.ts index 62e16288..9c142ff1 100644 --- a/src/helpers/topology.helpers.ts +++ b/src/helpers/topology.helpers.ts @@ -18,6 +18,7 @@ import { } from '../__generated__/topology-discovery.graphql'; import { ArangoDevice, + ArangoPtpDevice as DbArangoPtpDevice, ArangoEdge, ArangoEdgeWithStatus, EdgeWithStatus, @@ -141,7 +142,7 @@ export function getFilterQuery(filter?: FilterInput | null): FilterQuery | undef }; } -type CustomPtpDetails = Omit; +type CustomPtpDetails = Omit; type QueryNetDevice = NonNullable[0]>['node']>; type PhyDeviceWithoutInterfaces = Omit; type PtpDeviceWithoutInterfaces = Omit & { @@ -200,8 +201,8 @@ export function convertPhyDeviceToArangoDevice(phyDevice: PhyDeviceWithoutInterf }; } -export function getStatus(status: string | undefined): 'ok' | 'unknown' { - return status === 'ok' ? 'ok' : 'unknown'; +export function getStatus(status: 'OK' | 'UNKNOWN' | undefined): 'OK' | 'UNKNOWN' { + return status === 'OK' ? 'OK' : 'UNKNOWN'; } export function getPtpTopologyInterfaces(topologyDevices: PtpTopologyQuery) { @@ -223,11 +224,11 @@ export function getPtpTopologyInterfaces(topologyDevices: PtpTopologyQuery) { nodeId: node.name, details: { // eslint-disable-next-line @typescript-eslint/naming-convention - ptp_status: inode.details.ptp_status, + ptp_status: inode.details.ptpStatus, // eslint-disable-next-line @typescript-eslint/naming-convention - admin_oper_status: inode.details.admin_oper_status, + admin_oper_status: inode.details.adminOperStatus, // eslint-disable-next-line @typescript-eslint/naming-convention - ptsf_unusable: inode.details.ptsf_unusable, + ptsf_unusable: inode.details.ptsfUnusable, }, }; }) @@ -240,6 +241,23 @@ export function getPtpTopologyInterfaces(topologyDevices: PtpTopologyQuery) { export function makePtpDeviceDetails( details: NonNullable[0]>['node']>['details'], ): PtpDeviceDetails { + return { + clockType: details.clockType, + domain: details.domain, + ptpProfile: details.ptpProfile, + clockId: details.clockId, + parentClockId: details.parentClockId, + gmClockId: details.gmClockId, + clockClass: details.clockClass, + clockAccuracy: details.clockAccuracy, + clockVariance: details.clockVariance, + timeRecoveryStatus: details.timeRecoveryStatus, + globalPriority: details.globalPriority, + userPriority: details.userPriority, + }; +} + +export function makePtpDeviceDetailsFromArangoDetails(details: DbArangoPtpDevice['details']): PtpDeviceDetails { return { clockType: details.clock_type, domain: details.domain, @@ -283,9 +301,9 @@ export function makePtpTopologyNodes(ptpDevices?: PtpTopologyQuery) { name: interfaceNode.name, status: getStatus(interfaceNode.status), details: { - ptpStatus: interfaceNode.details.ptp_status, - adminOperStatus: interfaceNode.details.admin_oper_status, - ptsfUnusable: interfaceNode.details.ptsf_unusable, + ptpStatus: interfaceNode.details.ptpStatus, + adminOperStatus: interfaceNode.details.adminOperStatus, + ptsfUnusable: interfaceNode.details.ptsfUnusable, }, }; }) @@ -353,7 +371,7 @@ export function convertSynceDeviceToArangoDevice(synceDevice: SynceDeviceWithout _key: synceDevice.id, coordinates, synceDeviceDetails: { - selectedForUse: details.selected_for_use, + selectedForUse: details.selectedForUse, }, labels: labels ?? [], name, @@ -366,26 +384,26 @@ export type ArangoMplsDevice = Omit t != null) .map((t) => ({ - lspId: t.lsp_id, - fromDevice: t.from_device, - toDevice: t.to_device, + lspId: t.lspId, + fromDevice: t.fromDevice, + toDevice: t.toDevice, signalization: t.signalisation, uptime: t.uptime, })) ?? null, mplsData: - input.mpls_data + input.mplsData ?.filter((d): d is ApiMplsData => d != null) .map((d) => ({ - lspId: d.lsp_id, - inputlabel: d.in_label, - inputInterface: d.in_interface, - outputLabel: d.out_label, - outputInterface: d.out_interface, - mplsOperation: d.mpls_operation, - operState: d.oper_state, + lspId: d.lspId, + inputlabel: d.inLabel, + inputInterface: d.inInterface, + outputLabel: d.outLabel, + outputInterface: d.outInterface, + mplsOperation: d.mplsOperation, + operState: d.operState, signalization: d.signalisation, })) ?? null, }; @@ -830,7 +848,7 @@ export function getOldPtpTopologyDevices(devices: ArangoPtpDevice[], diffData: T if (isPtpTopologyDiff(diffData)) { const parsedOldDevices = diffData.deleted.PtpDevice.map((d) => ({ ...d, - ptpDeviceDetails: makePtpDeviceDetails(d.details), + ptpDeviceDetails: makePtpDeviceDetailsFromArangoDetails(d.details), })); oldDevices = devices @@ -839,7 +857,7 @@ export function getOldPtpTopologyDevices(devices: ArangoPtpDevice[], diffData: T (n) => !diffData.added.PtpDevice.map((d) => ({ ...d, - ptpDeviceDetails: makePtpDeviceDetails(d.details), + ptpDeviceDetails: makePtpDeviceDetailsFromArangoDetails(d.details), })).find((d) => n._id === d._id), ) // add devices removed from current topology @@ -849,11 +867,11 @@ export function getOldPtpTopologyDevices(devices: ArangoPtpDevice[], diffData: T const changedDevice = diffData.changed.PtpDevice.map((d) => ({ old: { ...d.old, - ptpDeviceDetails: makePtpDeviceDetails(d.old.details), + ptpDeviceDetails: makePtpDeviceDetailsFromArangoDetails(d.old.details), }, new: { ...d.new, - ptpDeviceDetails: makePtpDeviceDetails(d.new.details), + ptpDeviceDetails: makePtpDeviceDetailsFromArangoDetails(d.new.details), }, })).find((d) => d.old._id === n._id); if (!changedDevice) { @@ -1152,8 +1170,8 @@ export function makeTopologyNodes(dbDevices: PrismaDevice[], topologyDevices?: T return { id: toGraphId('GraphNode', node.id), name: node.name, - deviceType: node.details.device_type ?? null, - softwareVersion: node.details.sw_version ?? null, + deviceType: node.details.deviceType ?? null, + softwareVersion: node.details.swVersion ?? null, device: dbDevicesMap.get(node.name) ?? null, interfaces: node.phyInterfaces.edges?.map((e) => ({ @@ -1213,15 +1231,15 @@ export function getSynceTopologyInterfaces(topologyDevices: SynceTopologyQuery) nodeId: node.name, details: { // eslint-disable-next-line @typescript-eslint/naming-convention - synce_enabled: inode?.details?.synce_enabled ?? null, + synce_enabled: inode?.details?.synceEnabled ?? null, // eslint-disable-next-line @typescript-eslint/naming-convention - rx_quality_level: inode?.details?.rx_quality_level ?? null, + rx_quality_level: inode?.details?.rxQualityLevel ?? null, // eslint-disable-next-line @typescript-eslint/naming-convention - qualified_for_use: inode?.details?.qualified_for_use ?? null, + qualified_for_use: inode?.details?.qualifiedForUse ?? null, // eslint-disable-next-line @typescript-eslint/naming-convention - not_selected_due_to: inode?.details?.not_selected_due_to ?? null, + not_selected_due_to: inode?.details?.notSelectedDueTo ?? null, // eslint-disable-next-line @typescript-eslint/naming-convention - not_qualified_due_to: inode?.details?.not_qualified_due_to ?? null, + not_qualified_due_to: inode?.details?.notQualifiedDueTo ?? null, }, }; }) @@ -1271,7 +1289,7 @@ export function getDeviceInterfaceEdges(topologyDevices: TopologyDevicesQuery): _key: 'some_id', _from: d.node?.id ?? '', _to: i?.node?.id ?? '', - status: d.node?.status ?? 'unknown', + status: d.node?.status ?? 'UNKNOWN', })) ?? [], ) ?? [] ); @@ -1287,7 +1305,7 @@ export function getPtpDeviceInterfaceEdges(topologyDevices: PtpTopologyQuery): A _key: 'some_id', _from: d.node?.id ?? '', _to: i?.node?.id ?? '', - status: d.node?.status ?? 'unknown', + status: d.node?.status ?? 'UNKNOWN', })) ?? [], ) ?? [] ); @@ -1303,7 +1321,7 @@ export function getSynceDeviceInterfaceEdges(topologyDevices: SynceTopologyQuery _key: 'some_id', _from: d.node?.id ?? '', _to: i?.node?.id ?? '', - status: d.node?.status ?? 'unknown', + status: d.node?.status ?? 'UNKNOWN', })) ?? [], ) ?? [] ); @@ -1319,7 +1337,7 @@ export function getMplsDeviceInterfaceEdges(topologyDevices: MplsTopologyQuery): _key: 'some_id', _from: d.node?.id ?? '', _to: i?.node?.id ?? '', - status: d.node?.status ?? 'unknown', + status: d.node?.status ?? 'UNKNOWN', })) ?? [], ) ?? [] ); @@ -1339,7 +1357,7 @@ export function getNetDeviceInterfaceEdges(topologyDevices: NetTopologyQuery): A _key: 'some_id', _from: d.node.id, _to: i?.node?.id ?? '', - status: d.node.phyDevice.status ?? 'unknown', + status: d.node.phyDevice.status ?? 'UNKNOWN', }; } return null; @@ -1490,7 +1508,7 @@ export function makeSynceDeviceDetails( device: NonNullable[0]>['node']>, ): SynceDeviceDetails { return { - selectedForUse: device.details.selected_for_use, + selectedForUse: device.details.selectedForUse, }; } @@ -1521,11 +1539,11 @@ export function makeSynceTopologyNodes(synceDevices?: SynceTopologyQuery) { name: interfaceNode.name, status: getStatus(interfaceNode.status), details: { - synceEnabled: interfaceNode.details?.synce_enabled, - rxQualityLevel: interfaceNode.details?.rx_quality_level, - qualifiedForUse: interfaceNode.details?.qualified_for_use, - notSelectedDueTo: interfaceNode.details?.not_selected_due_to, - notQualifiedDueTo: interfaceNode.details?.not_qualified_due_to, + synceEnabled: interfaceNode.details?.synceEnabled, + rxQualityLevel: interfaceNode.details?.rxQualityLevel, + qualifiedForUse: interfaceNode.details?.qualifiedForUse, + notSelectedDueTo: interfaceNode.details?.notSelectedDueTo, + notQualifiedDueTo: interfaceNode.details?.notQualifiedDueTo, }, }; }) @@ -1898,22 +1916,22 @@ export function makeMplsTopologyNodes(mplsDevices?: MplsTopologyQuery) { .filter(omitNullValue) ?? [], mplsDeviceDetails: { lspTunnels: - node.details.lsp_tunnels?.map((tunnel) => ({ - lspId: tunnel?.lsp_id ?? '', - fromDevice: tunnel?.from_device ?? null, - toDevice: tunnel?.to_device ?? null, + node.details.lspTunnels?.map((tunnel) => ({ + lspId: tunnel?.lspId ?? '', + fromDevice: tunnel?.fromDevice ?? null, + toDevice: tunnel?.toDevice ?? null, uptime: tunnel?.uptime ?? null, signalization: tunnel?.signalisation ?? null, })) ?? null, mplsData: - node.details.mpls_data?.map((d) => ({ - lspId: d?.lsp_id ?? '', - inputLabel: d?.in_label ?? null, - inputInterface: d?.in_interface ?? null, - outputInterface: d?.out_interface ?? null, - outputLabel: d?.out_label ?? null, - operState: d?.oper_state ?? null, - mplsOperation: d?.mpls_operation ?? null, + node.details.mplsData?.map((d) => ({ + lspId: d?.lspId ?? '', + inputLabel: d?.inLabel ?? null, + inputInterface: d?.inInterface ?? null, + outputInterface: d?.outInterface ?? null, + outputLabel: d?.outLabel ?? null, + operState: d?.operState ?? null, + mplsOperation: d?.mplsOperation ?? null, })) ?? null, }, }; @@ -1979,11 +1997,11 @@ export function makeMplsTopologyEdges(mplsDevices?: MplsTopologyQuery) { } export function convertDiscoveryMplsPathToApiMplsPath(mplsPathQuery: MplsPathQuery): NexusGenObjects['LspPath'] { - const { path: apiPath, lsp_metadata: apiMetadata } = mplsPathQuery.mplsLspPath; + const { path: apiPath, lspMetadata: apiMetadata } = mplsPathQuery.mplsLspPath; const path = apiPath?.filter(omitNullValue) ?? []; const metadata = { - fromDevice: apiMetadata?.from_device, - toDevice: apiMetadata?.to_device, + fromDevice: apiMetadata?.fromDevice, + toDevice: apiMetadata?.toDevice, signalization: apiMetadata?.signalisation, uptime: apiMetadata?.uptime, }; diff --git a/src/schema/api.graphql b/src/schema/api.graphql index fe3957e7..884e86ce 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -419,8 +419,8 @@ type GraphEdge { } enum GraphEdgeStatus { - ok - unknown + OK + UNKNOWN } type GraphNode implements BaseGraphNode { @@ -927,18 +927,18 @@ type TopologyCommonNodes { } enum TopologyLayer { - EthTopology - MplsTopology - PhysicalTopology - PtpTopology + ETH_TOPOLOGY + MPLS_TOPOLOGY + PHYSICAL_TOPOLOGY + PTP_TOPOLOGY } enum TopologyType { - EthTopology - MplsTopology - NetworkTopology - PhysicalTopology - PtpTopology + ETH_TOPOLOGY + MPLS_TOPOLOGY + NETWORK_TOPOLOGY + PHYSICAL_TOPOLOGY + PTP_TOPOLOGY } type Transaction { diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index e7a94a36..444add32 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -227,13 +227,13 @@ export interface NexusGenEnums { DeviceServiceState: 'IN_SERVICE' | 'OUT_OF_SERVICE' | 'PLANNING'; DeviceSize: 'LARGE' | 'MEDIUM' | 'SMALL'; DeviceSource: 'DISCOVERED' | 'IMPORTED' | 'MANUAL'; - GraphEdgeStatus: 'ok' | 'unknown'; + GraphEdgeStatus: 'OK' | 'UNKNOWN'; Signalization: 'LDP' | 'RSVP'; SortDeviceBy: 'discoveredAt' | 'modelVersion' | 'name'; SortDirection: 'ASC' | 'DESC'; SortStreamBy: 'createdAt' | 'deviceName' | 'streamName'; - TopologyLayer: 'EthTopology' | 'MplsTopology' | 'PhysicalTopology' | 'PtpTopology'; - TopologyType: 'EthTopology' | 'MplsTopology' | 'NetworkTopology' | 'PhysicalTopology' | 'PtpTopology'; + TopologyLayer: 'ETH_TOPOLOGY' | 'MPLS_TOPOLOGY' | 'PHYSICAL_TOPOLOGY' | 'PTP_TOPOLOGY'; + TopologyType: 'ETH_TOPOLOGY' | 'MPLS_TOPOLOGY' | 'NETWORK_TOPOLOGY' | 'PHYSICAL_TOPOLOGY' | 'PTP_TOPOLOGY'; } export interface NexusGenScalars { diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 6869a182..090157b2 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -63,7 +63,7 @@ export const FilterTopologyInput = inputObjectType({ export const GraphInterfaceStatus = enumType({ name: 'GraphEdgeStatus', - members: ['ok', 'unknown'], + members: ['OK', 'UNKNOWN'], }); export const PtpGraphNodeInterfaceDetails = objectType({ @@ -510,7 +510,7 @@ export const PhyTopologyVersionDataQuery = extendType({ const interfaceEdges = getDeviceInterfaceEdges(topologyDevicesResult); const { version } = args; - const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'PhysicalTopology'); + const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'PHYSICAL_TOPOLOGY'); return makeTopologyDiff(topologyDiff, currentNodes, currentEdges, interfaces, interfaceEdges); }, @@ -543,7 +543,7 @@ export const PtpTopologyVersionDataQuery = extendType({ const interfaceEdges = getPtpDeviceInterfaceEdges(topologyDevicesResult); const { version } = args; - const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'PtpTopology'); + const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'PTP_TOPOLOGY'); return makePtpTopologyDiff(topologyDiff, currentNodes, currentEdges, interfaces, interfaceEdges); }, @@ -576,7 +576,7 @@ export const TopologyVersionDataQuery = extendType({ const interfaceEdges = getSynceDeviceInterfaceEdges(topologyDevicesResult); const { version } = args; - const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'EthTopology'); + const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'ETH_TOPOLOGY'); return makeSynceTopologyDiff(topologyDiff, currentNodes, currentEdges, interfaces, interfaceEdges); }, @@ -609,7 +609,7 @@ export const MplsTopologyVersionDataQuery = extendType({ const interfaceEdges = getMplsDeviceInterfaceEdges(topologyDevicesResult); const { version } = args; - const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'MplsTopology'); + const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'MPLS_TOPOLOGY'); return makeMplsTopologyDiff(topologyDiff, currentNodes, currentEdges, interfaces, interfaceEdges); }, @@ -628,7 +628,7 @@ export const GraphNodeCoordinatesInput = inputObjectType({ export const TopologyLayer = enumType({ name: 'TopologyLayer', - members: ['PhysicalTopology', 'PtpTopology', 'EthTopology', 'MplsTopology'], + members: ['PHYSICAL_TOPOLOGY', 'PTP_TOPOLOGY', 'ETH_TOPOLOGY', 'MPLS_TOPOLOGY'], }); export const UpdateGraphNodeCooordinatesInput = inputObjectType({ @@ -666,7 +666,7 @@ export const UpdateGraphNodeCoordinatesMutation = extendType({ const apiParams = input.coordinates.map((i) => ({ device: i.deviceName, x: i.x, y: i.y })) || []; const response = await topologyDiscoveryGraphQLAPI.updateCoordinates( apiParams, - input.layer ?? 'PhysicalTopology', + input.layer ?? 'PHYSICAL_TOPOLOGY', ); return { deviceNames: response, @@ -765,7 +765,7 @@ export const NetTopologyVersionDataQuery = extendType({ const interfaceEdges = getNetDeviceInterfaceEdges(topologyDevicesResult); const { version } = args; - const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'NetworkTopology'); + const topologyDiff = await topologyDiscoveryGraphQLAPI.getTopologyDiff(version, 'NETWORK_TOPOLOGY'); return makeNetTopologyDiff(topologyDiff, currentNodes, currentEdges, interfaces, interfaceEdges); }, @@ -903,7 +903,7 @@ export const DeviceMetadata = objectType({ export const TopologyType = enumType({ name: 'TopologyType', - members: ['PhysicalTopology', 'PtpTopology', 'EthTopology', 'NetworkTopology', 'MplsTopology'], + members: ['PHYSICAL_TOPOLOGY', 'PTP_TOPOLOGY', 'ETH_TOPOLOGY', 'NETWORK_TOPOLOGY', 'MPLS_TOPOLOGY'], }); export const PolygonInputType = inputObjectType({ @@ -998,8 +998,8 @@ export const deviceNeighborQuery = queryField('deviceNeighbor', { const neighborDevices = deviceNeighborsResult.neighbors?.map((device) => ({ - deviceName: device.device_name, - deviceId: device.device_id, + deviceName: device.deviceName, + deviceId: device.deviceId, })) || []; return { neighbors: neighborDevices }; @@ -1064,9 +1064,9 @@ export const MplsLspCountQuery = queryField('mplsLspCount', { return { counts: result.mplsLspCount.map((c) => ({ - target: c?.to_device ?? null, - incomingLsps: c?.incoming_lsps ?? null, - outcomingLsps: c?.outcoming_lsps ?? null, + target: c?.toDevice ?? null, + incomingLsps: c?.incomingLsps ?? null, + outcomingLsps: c?.outcomingLsps ?? null, })), }; }, From a4d2227f68296a138008b88b26a44a7933a5afc2 Mon Sep 17 00:00:00 2001 From: Martin Sottnik Date: Mon, 9 Sep 2024 09:07:14 +0200 Subject: [PATCH 16/18] remove device from cache before reading status (#469) --- src/schema/device.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/schema/device.ts b/src/schema/device.ts index f7af0323..3116f791 100644 --- a/src/schema/device.ts +++ b/src/schema/device.ts @@ -22,6 +22,7 @@ import { getCachedDeviceInstallStatus, installDeviceCache, installMultipleDevicesCache, + UniconfigCache, uninstallDeviceCache, uninstallMultipleDevicesCache, } from '../external-api/uniconfig-cache'; @@ -393,7 +394,13 @@ export const UpdateDeviceMutation = extendType({ if (dbDevice == null) { throw new Error('device not found'); } + const uniconfigURL = await getUniconfigURL(prisma, dbDevice.uniconfigZoneId); + + // FR-333 - force cache to read new value from uniconfig + const cache = UniconfigCache.getInstance(); + cache.delete(uniconfigURL, dbDevice.name); + const isInstalled = await getCachedDeviceInstallStatus(uniconfigURL, dbDevice.name); if (isInstalled) { throw new Error('device is installed in UniConfig'); From b70558d9db4e5c650bd4a6224d8989112aa68f18 Mon Sep 17 00:00:00 2001 From: Peto Date: Thu, 26 Sep 2024 10:28:14 +0200 Subject: [PATCH 17/18] added query to get topology of specific device --- .../topology-discovery.graphql.ts | 57 ++++++++++++++++++- .../topology-discovery-graphql.ts | 18 ++++++ src/schema/api.graphql | 10 ++++ src/schema/nexus-typegen.ts | 33 +++++++++++ src/schema/topology.ts | 31 ++++++++++ 5 files changed, 146 insertions(+), 3 deletions(-) diff --git a/src/__generated__/topology-discovery.graphql.ts b/src/__generated__/topology-discovery.graphql.ts index b2ed66d3..db36b3e3 100644 --- a/src/__generated__/topology-discovery.graphql.ts +++ b/src/__generated__/topology-discovery.graphql.ts @@ -96,7 +96,7 @@ export type DeviceMetadata = Node & { /** Model of the device (XR, ASR). */ model: Maybe; /** Protocol used for management for the device (cli, netconf, gnmi). */ - protocolType: Maybe; + protocolType: Maybe>; /** Vendor of the device (ex. Cisco). */ vendor: Maybe; /** Version of the device software (ex. 6.0.1). */ @@ -393,6 +393,8 @@ export type Mutation = { * Response contains version of the debug library. */ enableRemoteDebugSession: Scalars['String']; + /** Refresh the coordinates of nodes in the specified topology by using the ForceAtlas2 algorithm. */ + refreshCoordinates: RefreshCoordinatesResponse; /** * Synchronization of the devices in the specified topology. * Topology represents an abstraction layer of observed network from the operational view @@ -428,7 +430,12 @@ export type MutationEnableRemoteDebugSessionArgs = { host: Scalars['String']; port?: InputMaybe; stderrToServer?: InputMaybe; - stdoutToTerver?: InputMaybe; + stdoutToServer?: InputMaybe; +}; + + +export type MutationRefreshCoordinatesArgs = { + topologyType?: InputMaybe; }; @@ -643,6 +650,17 @@ export type Node = { id: Scalars['ID']; }; +/** Represents the coordinates of a specific node in the topology. */ +export type NodeCoordinates = { + __typename?: 'NodeCoordinates'; + /** Name of the node in the topology. */ + nodeId: Scalars['String']; + /** Refreshed horizontal coordinate of the node on the graph. Value is between 0.0 and 1.0. */ + x: Scalars['Float']; + /** Refreshed vertical coordinate of the node on the graph. Value is between 0.0 and 1.0. */ + y: Scalars['Float']; +}; + /** Information about a node that is part of the computed path. */ export type NodeInfo = { __typename?: 'NodeInfo'; @@ -1143,6 +1161,11 @@ export type Query = { * If invalid device identifier is specified, error is returned. */ syncePathToGm: SyncePath; + /** + * Find identifiers of the topologies where the specified device is present. + * The query returns a list in which each entry contains a topology identifier and a device identifier. + */ + topologies: Maybe>; /** * Computation of the diff between two databases per collections - created, deleted, and changed entries. * Only documents that belong to the specified topology are included in the diff. @@ -1260,6 +1283,11 @@ export type QuerySyncePathToGmArgs = { }; +export type QueryTopologiesArgs = { + deviceName: Scalars['String']; +}; + + export type QueryTopologyDiffArgs = { collectionType: TopologyType; newDb: Scalars['String']; @@ -1275,6 +1303,13 @@ export type QueryTopologyOverlayArgs = { secondTopology: TopologyType; }; +/** Response containing a list of nodes with refreshed coordinates. */ +export type RefreshCoordinatesResponse = { + __typename?: 'RefreshCoordinatesResponse'; + /** List of refreshed nodes with their new coordinates. */ + nodes: Array>; +}; + /** Computed routing path from source to destination device. */ export type RoutingPath = { __typename?: 'RoutingPath'; @@ -1510,6 +1545,15 @@ export type SyncePathOutputCollections = /** Include SynceInterface nodes in the returned path. */ | 'SYNCE_INTERFACE'; +/** Topology and device identifier of a device. */ +export type TopologyDevice = { + __typename?: 'TopologyDevice'; + /** Topology-specific device identifier. */ + deviceId: Scalars['ID']; + /** Identifier of the topology in which device is present. */ + topologyId: TopologyType; +}; + export type TopologyOverlayDevice = { __typename?: 'TopologyOverlayDevice'; /** Unique identifier of the object. */ @@ -1734,7 +1778,7 @@ export type DeviceMetadataQueryVariables = Exact<{ }>; -export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: string | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; +export type DeviceMetadataQuery = { __typename?: 'Query', deviceMetadata: { __typename?: 'MetadataConnection', edges: Array<{ __typename?: 'DeviceMetadataEdge', node: { __typename?: 'DeviceMetadata', id: string, deviceName: string, deviceType: string | null, model: string | null, vendor: string | null, version: string | null, protocolType: Array | null, geoLocation: { __typename?: 'DeviceGeoLocation', bbox: Array | null, coordinates: Array, type: GeometryType } | null } | null } | null> | null } }; export type MplsDevicePartsFragment = { __typename?: 'MplsDevice', id: string, name: string, status: NodeStatus, labels: Array | null, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, status: NodeStatus, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, mplsDevice: { __typename?: 'MplsDevice', id: string, name: string, coordinates: { __typename?: 'Coordinates', x: number, y: number }, mplsInterfaces: { __typename?: 'MplsInterfaceConnection', edges: Array<{ __typename?: 'MplsInterfaceEdge', node: { __typename?: 'MplsInterface', id: string, name: string, mplsLinks: { __typename?: 'MplsLinkConnection', edges: Array<{ __typename?: 'MplsLinkEdge', link: string | null, node: { __typename?: 'MplsInterface', id: string, name: string } | null } | null> | null } | null } | null } | null> | null } } | null } | null } | null> | null } | null } | null } | null> | null }, details: { __typename?: 'MplsDeviceDetails', routerId: string | null, mplsData: Array<{ __typename?: 'MplsData', lspId: string, inLabel: number | null, inInterface: string | null, outInterface: string | null, outLabel: number | null, mplsOperation: MplsOperation | null, operState: string | null, signalisation: Signalisation | null } | null> | null, lspTunnels: Array<{ __typename?: 'LspTunnel', lspId: string, fromDevice: string | null, toDevice: string | null, signalisation: Signalisation, uptime: number | null } | null> | null } }; @@ -1769,3 +1813,10 @@ export type NeighborsQueryVariables = Exact<{ export type NeighborsQuery = { __typename?: 'Query', neighbors: Array<{ __typename?: 'Neighbor', deviceId: string, deviceName: string }> | null }; + +export type TopologiesQueryVariables = Exact<{ + deviceName: Scalars['String']; +}>; + + +export type TopologiesQuery = { __typename?: 'Query', topologies: Array<{ __typename?: 'TopologyDevice', topologyId: TopologyType, deviceId: string }> | null }; diff --git a/src/external-api/topology-discovery-graphql.ts b/src/external-api/topology-discovery-graphql.ts index cf2d0c40..a4d03d79 100644 --- a/src/external-api/topology-discovery-graphql.ts +++ b/src/external-api/topology-discovery-graphql.ts @@ -30,6 +30,7 @@ import { MplsPathQueryVariables, NeighborsQuery, NeighborsQueryVariables, + TopologiesQuery, } from '../__generated__/topology-discovery.graphql'; import { TopologyDiffOutput, decodeTopologyDiffOutput } from './topology-network-types'; @@ -576,6 +577,15 @@ const MAP_NEIGHBORS = gql` } `; +const TOPOLOGIES = gql` + query Topologies($deviceName: String!) { + topologies(deviceName: $deviceName) { + topologyId + deviceId + } + } +`; + function getTopologyDiscoveryApi() { if (!config.topologyEnabled) { return undefined; @@ -615,6 +625,13 @@ function getTopologyDiscoveryApi() { return response; } + async function getTopologyOfDevice(deviceName: string): Promise { + const response = await client.request(TOPOLOGIES, { + deviceName, + }); + return response; + } + async function getTopologyDiff(version: string, collectionType: TopologyType): Promise { const response = await client.request(GET_TOPOLOGY_DIFF, { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -759,6 +776,7 @@ function getTopologyDiscoveryApi() { getMplsLspCount, getLspPath, getMapNeighbors, + getTopologyOfDevice, }; } diff --git a/src/schema/api.graphql b/src/schema/api.graphql index 884e86ce..19cee139 100644 --- a/src/schema/api.graphql +++ b/src/schema/api.graphql @@ -767,6 +767,7 @@ type Query { last: Int orderBy: DeviceOrderByInput ): DeviceConnection! + devicesTopology(deviceName: String!): Topologies kafkaHealthCheck: IsOkResponse labels(after: String, before: String, filter: FilterLabelsInput, first: Int, last: Int): LabelConnection! locations(after: String, before: String, first: Int, last: Int): LocationConnection! @@ -917,6 +918,10 @@ type SynceTopologyVersionData { nodes: [SynceGraphNode!]! } +type Topologies { + topologies: [TopologyOfDevice] +} + type Topology { edges: [GraphEdge!]! nodes: [GraphNode!]! @@ -933,6 +938,11 @@ enum TopologyLayer { PTP_TOPOLOGY } +type TopologyOfDevice { + deviceId: String! + topologyId: String! +} + enum TopologyType { ETH_TOPOLOGY MPLS_TOPOLOGY diff --git a/src/schema/nexus-typegen.ts b/src/schema/nexus-typegen.ts index 444add32..f41f7c10 100644 --- a/src/schema/nexus-typegen.ts +++ b/src/schema/nexus-typegen.ts @@ -799,6 +799,10 @@ export interface NexusGenObjects { edges: NexusGenRootTypes['GraphVersionEdge'][]; // [GraphVersionEdge!]! nodes: NexusGenRootTypes['SynceGraphNode'][]; // [SynceGraphNode!]! }; + Topologies: { + // root type + topologies?: Array | null; // [TopologyOfDevice] + }; Topology: { // root type edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! @@ -808,6 +812,11 @@ export interface NexusGenObjects { // root type commonNodes: string[]; // [String!]! }; + TopologyOfDevice: { + // root type + deviceId: string; // String! + topologyId: string; // String! + }; Transaction: { // root type changes: NexusGenRootTypes['TransactionChange'][]; // [TransactionChange!]! @@ -1474,6 +1483,7 @@ export interface NexusGenFieldTypes { deviceMetadata: NexusGenRootTypes['DeviceMetadata'] | null; // DeviceMetadata deviceNeighbor: NexusGenRootTypes['DeviceNeighbors'] | null; // DeviceNeighbors devices: NexusGenRootTypes['DeviceConnection']; // DeviceConnection! + devicesTopology: NexusGenRootTypes['Topologies'] | null; // Topologies kafkaHealthCheck: NexusGenRootTypes['IsOkResponse'] | null; // IsOkResponse labels: NexusGenRootTypes['LabelConnection']; // LabelConnection! locations: NexusGenRootTypes['LocationConnection']; // LocationConnection! @@ -1589,6 +1599,10 @@ export interface NexusGenFieldTypes { edges: NexusGenRootTypes['GraphVersionEdge'][]; // [GraphVersionEdge!]! nodes: NexusGenRootTypes['SynceGraphNode'][]; // [SynceGraphNode!]! }; + Topologies: { + // field return type + topologies: Array | null; // [TopologyOfDevice] + }; Topology: { // field return type edges: NexusGenRootTypes['GraphEdge'][]; // [GraphEdge!]! @@ -1598,6 +1612,11 @@ export interface NexusGenFieldTypes { // field return type commonNodes: string[]; // [String!]! }; + TopologyOfDevice: { + // field return type + deviceId: string; // String! + topologyId: string; // String! + }; Transaction: { // field return type changes: NexusGenRootTypes['TransactionChange'][]; // [TransactionChange!]! @@ -2264,6 +2283,7 @@ export interface NexusGenFieldTypeNames { deviceMetadata: 'DeviceMetadata'; deviceNeighbor: 'DeviceNeighbors'; devices: 'DeviceConnection'; + devicesTopology: 'Topologies'; kafkaHealthCheck: 'IsOkResponse'; labels: 'LabelConnection'; locations: 'LocationConnection'; @@ -2379,6 +2399,10 @@ export interface NexusGenFieldTypeNames { edges: 'GraphVersionEdge'; nodes: 'SynceGraphNode'; }; + Topologies: { + // field return type name + topologies: 'TopologyOfDevice'; + }; Topology: { // field return type name edges: 'GraphEdge'; @@ -2388,6 +2412,11 @@ export interface NexusGenFieldTypeNames { // field return type name commonNodes: 'String'; }; + TopologyOfDevice: { + // field return type name + deviceId: 'String'; + topologyId: 'String'; + }; Transaction: { // field return type name changes: 'TransactionChange'; @@ -2679,6 +2708,10 @@ export interface NexusGenArgTypes { last?: number | null; // Int orderBy?: NexusGenInputs['DeviceOrderByInput'] | null; // DeviceOrderByInput }; + devicesTopology: { + // args + deviceName: string; // String! + }; labels: { // args after?: string | null; // String diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 090157b2..8e4714fa 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -1009,6 +1009,37 @@ export const deviceNeighborQuery = queryField('deviceNeighbor', { }, }); +export const TopologyOfDevice = objectType({ + name: 'TopologyOfDevice', + definition: (t) => { + t.nonNull.string('topologyId'); + t.nonNull.string('deviceId'); + }, +}); + +export const Topologies = objectType({ + name: 'Topologies', + definition: (t) => { + t.list.field('topologies', { + type: TopologyOfDevice, + }); + }, +}); + +export const TopologyOfDevicesQuery = queryField('devicesTopology', { + type: Topologies, + args: { + deviceName: nonNull(stringArg()), + }, + resolve: async (_, args, { topologyDiscoveryGraphQLAPI }) => { + const topologyOfDevicesResult = await topologyDiscoveryGraphQLAPI?.getTopologyOfDevice(args.deviceName); + + console.log(topologyOfDevicesResult); + + return topologyOfDevicesResult ?? { topologies: [] }; + }, +}); + export const MplsTopology = objectType({ name: 'MplsTopology', definition: (t) => { From e3c5b482a766e6cdf1368110da1494d8cd632d0a Mon Sep 17 00:00:00 2001 From: Peto Date: Thu, 26 Sep 2024 10:39:00 +0200 Subject: [PATCH 18/18] removed console.log --- src/schema/topology.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/schema/topology.ts b/src/schema/topology.ts index 8e4714fa..30a10586 100644 --- a/src/schema/topology.ts +++ b/src/schema/topology.ts @@ -1034,8 +1034,6 @@ export const TopologyOfDevicesQuery = queryField('devicesTopology', { resolve: async (_, args, { topologyDiscoveryGraphQLAPI }) => { const topologyOfDevicesResult = await topologyDiscoveryGraphQLAPI?.getTopologyOfDevice(args.deviceName); - console.log(topologyOfDevicesResult); - return topologyOfDevicesResult ?? { topologies: [] }; }, });