From a9cdb25805f18598cbd56220231b556e195319aa Mon Sep 17 00:00:00 2001 From: maayarosama Date: Wed, 23 Oct 2024 14:39:18 +0300 Subject: [PATCH] Updating networklight class in primitives --- .../src/primitives/networklight.ts | 267 ++++++++++-------- 1 file changed, 153 insertions(+), 114 deletions(-) diff --git a/packages/grid_client/src/primitives/networklight.ts b/packages/grid_client/src/primitives/networklight.ts index e1cd9c47a7..979ebdf59c 100644 --- a/packages/grid_client/src/primitives/networklight.ts +++ b/packages/grid_client/src/primitives/networklight.ts @@ -12,25 +12,30 @@ import { TFClient } from "../clients/tf-grid/client"; import { GqlNodeContract } from "../clients/tf-grid/contracts"; import { GridClientConfig } from "../config"; import { events } from "../helpers/events"; -import { generateRandomHexSeed } from "../helpers/utils"; +import { formatErrorMessage, generateRandomHexSeed } from "../helpers/utils"; import { validateHexSeed } from "../helpers/validator"; -import { ContractStates, MyceliumNetworkModel } from "../modules"; -import { BackendStorage, BackendStorageType } from "../storage/backend"; +import { MyceliumNetworkModel } from "../modules"; +import { BackendStorage } from "../storage/backend"; import { NetworkLight } from "../zos"; import { Deployment } from "../zos/deployment"; import { Workload, WorkloadTypes } from "../zos/workload"; +import { Nodes } from "./nodes"; class Node { node_id: number; contract_id: number; reserved_ips: string[] = []; } - +interface NetworkMetadata { + version: number; +} class ZNetworkLight { node: Node; + capacity: Nodes; deployments: Deployment[] = []; network: NetworkLight; contracts: Required[]; + reservedSubnets: string[] = []; backendStorage: BackendStorage; static newContracts: GqlNodeContract[] = []; static deletedContracts: number[] = []; @@ -56,37 +61,32 @@ class ZNetworkLight { config.seed, ); this.rmb = new RMB(config.rmbClient); - // this.capacity = new Nodes(this.config.graphqlURL, this.config.proxyURL, this.config.rmbClient); + this.capacity = new Nodes(this.config.graphqlURL, this.config.proxyURL, this.config.rmbClient); this.tfClient = config.tfclient; } - private async saveIfKVStoreBackend(extrinsics) { - if (this.config.backendStorageType === BackendStorageType.tfkvstore && extrinsics && extrinsics.length > 0) { - extrinsics = extrinsics.filter(e => e !== undefined); - if (extrinsics.length > 0) { - await this.tfClient.connect(); - await this.tfClient.applyAllExtrinsics(extrinsics); - } + private getUpdatedMetadata(nodeId: number, metadata: string): string { + if (this.node.node_id === nodeId) { + const parsedMetadata: NetworkMetadata = JSON.parse(metadata || "{}"); + parsedMetadata.version = 4; + return JSON.stringify(parsedMetadata); } + + return metadata; + } + + updateWorkload(nodeId: number, workload: Workload): Workload { + workload.data = this.getUpdatedNetwork(workload.data); + workload.metadata = this.getUpdatedMetadata(nodeId, workload.metadata); + return workload; + } + getUpdatedNetwork(znet_light): NetworkLight { + if (this.network.subnet === znet_light.subnet) { + return this.network; + } + + return znet_light; } - // Check this later - // private getUpdatedMetadata(nodeId: number, metadata: string): string { - // for (const node of this.nodes) { - // if (node.node_id === nodeId) { - // const parsedMetadata: NetworkMetadata = JSON.parse(metadata || "{}"); - // parsedMetadata.version = 3; - // parsedMetadata.user_accesses = this.userAccesses; - // return JSON.stringify(parsedMetadata); - // } - // } - // return metadata; - // } - - // updateWorkload(nodeId: number, workload: Workload): Workload { - // workload.data = this.getUpdatedNetwork(workload.data); - // workload.metadata = this.getUpdatedMetadata(nodeId, workload.metadata); - // return workload; - // } async addNode( nodeId: number, @@ -96,20 +96,15 @@ class ZNetworkLight { myceliumSeeds: MyceliumNetworkModel, ): Promise { events.emit("logs", `Adding node ${nodeId} to network ${this.name}`); - // const keypair = this.generateWireguardKeypair(); - const znet_light = new NetworkLight(); + let znet_light = new NetworkLight(); if (!subnet) { znet_light.subnet = this.getFreeSubnet(); } else { znet_light.subnet = subnet; } - // Check this subnet=iprange? - // znet.ip_range = this.ipRange; - znet_light["node_id"] = nodeId; if (mycelium) { - // const myceliumNetworkSeed = myceliumSeeds.find(item => item.nodeId === nodeId); let seed = generateRandomHexSeed(32); if (myceliumSeeds?.seed) { seed = myceliumSeeds.seed; @@ -123,11 +118,7 @@ class ZNetworkLight { } this.network = znet_light; - - // Check this later - // await this.generatePeers(); - // this.updateNetworkDeployments(); - // znet = this.getUpdatedNetwork(znet); + znet_light = this.getUpdatedNetwork(znet_light); const znet_light_workload = new Workload(); znet_light_workload.version = 0; @@ -142,26 +133,11 @@ class ZNetworkLight { return znet_light_workload; } - getUpdatedNetwork(znet_light): NetworkLight { - if (this.network.subnet === znet_light.subnet) { - return this.network; - } - - return znet_light; - } - _fromObj(net: NetworkLight): NetworkLight { const znet_light = plainToInstance(NetworkLight, net); return znet_light; } - getPublicKey(privateKey: string): string { - const privKey = Buffer.from(privateKey, "base64"); - const keypair = TweetNACL.box.keyPair.fromSecretKey(privKey); - return Buffer.from(keypair.publicKey).toString("base64"); - } - - // Check this what does it do getFreeIP(node_id: number, subnet = ""): string | undefined { let ip; @@ -181,6 +157,7 @@ class ZNetworkLight { throw new ValidationError(`node_id is not in the network. Please add it first.`); } } + validateUserIP(node_id: number, ip_address = "") { const nodeSubnet = this.getNodeSubnet(node_id); const ip = Addr(ip_address); @@ -193,6 +170,7 @@ class ZNetworkLight { return ip_address; } } + getNodeSubnet(node_id: number): string | undefined { if (this.network["node_id"] === node_id) { return this.network.subnet; @@ -200,33 +178,33 @@ class ZNetworkLight { } getFreeSubnet(): string { - const subnet = Addr(this.ipRange).mask(24).nextSibling().nextSibling(); + const reservedSubnets = this.getReservedSubnets(); + let subnet = Addr(this.ipRange).mask(24).nextSibling().nextSibling(); + while (reservedSubnets.includes(subnet.toString())) { + subnet = subnet.nextSibling(); + } + this.reservedSubnets.push(subnet.toString()); return subnet.toString(); } - // Check this later - getNetworksPath() { - // Check this Should we change to networklight - return PATH.join(this.config.storePath, "networks"); - } + getReservedSubnets(): string[] { + const subnet = this.getNodeSubnet(this.node.node_id); + if (subnet && !this.reservedSubnets.includes(subnet)) { + this.reservedSubnets.push(subnet); + } - // Check this - async getNetwork() { - const path = this.getNetworksPath(); - return await this.backendStorage.load(PATH.join(path, this.name, "info.json")); + return this.reservedSubnets; } private async getMyNetworkContracts(fetch = false) { if (fetch || !this.contracts) { - // Check this Should we change to networklight let contracts = await this.tfClient.contracts.listMyNodeContracts({ graphqlURL: this.config.graphqlURL, - type: "network", + type: "network-light", }); const alreadyFetchedContracts: GqlNodeContract[] = []; for (const contract of ZNetworkLight.newContracts) { - // Check this Should we change to networklight - if (contract.parsedDeploymentData!.type !== "network") continue; + if (contract.parsedDeploymentData!.type !== "network-light") continue; const c = contracts.filter(c => +c.contractID === +contract.contractID); if (c.length > 0) { alreadyFetchedContracts.push(contract); @@ -254,6 +232,108 @@ class ZNetworkLight { return this.contracts; } + async load(): Promise { + if (!(await this.exists())) { + return; + } + events.emit("logs", `Loading network ${this.name}`); + + await this.loadNetworkFromContracts(); + + // else { + // const network = await this.getNetwork(); + // if (network["ip_range"] !== this.ipRange) { + // throw new ValidationError(`The same network name ${this.name} with a different ip range already exists.`); + // } + + // await this.tfClient.connect(); + // for (const node of network["nodes"]) { + // const contract = await this.tfClient.contracts.get({ + // id: node.contract_id, + // }); + // if (contract === null) continue; + // const node_twin_id = await this.capacity.getNodeTwinId(node.node_id); + // const payload = JSON.stringify({ contract_id: node.contract_id }); + // let res; + // try { + // res = await this.rmb.request([node_twin_id], "zos.deployment.get", payload); + // } catch (e) { + // (e as Error).message = formatErrorMessage(`Failed to load network deployment ${node.contract_id}`, e); + // throw e; + // } + // res["node_id"] = node.node_id; + // for (const workload of res["workloads"]) { + // if ( + // workload["type"] !== WorkloadTypes.network || + // !Addr(this.ipRange).contains(Addr(workload["data"]["subnet"])) + // ) { + // continue; + // } + // if (workload.result.state === "deleted") { + // continue; + // } + // const znet = this._fromObj(workload["data"]); + // znet["node_id"] = node.node_id; + // const n: Node = node; + // this.nodes.push(n); + // this.networks.push(znet); + // this.deployments.push(res); + // } + // } + // } + } + + private async getReservedIps(nodeId: number): Promise { + const node_twin_id = await this.capacity.getNodeTwinId(nodeId); + const payload = JSON.stringify({ network_name: this.name }); + let reservedIps: string[]; + try { + reservedIps = await this.rmb.request([node_twin_id], "zos.network.list_private_ips", payload); + } catch (e) { + (e as Error).message = formatErrorMessage(`Failed to list reserved ips from node ${nodeId}`, e); + throw e; + } + return reservedIps; + } + + private async loadNetworkFromContracts() { + const contracts = await this.getDeploymentContracts(this.name); + for (const contract of contracts) { + const node_twin_id = await this.capacity.getNodeTwinId(contract.nodeID); + const payload = JSON.stringify({ contract_id: +contract.contractID }); + let res: Deployment; + try { + res = await this.rmb.request([node_twin_id], "zos.deployment.get", payload); + } catch (e) { + (e as Error).message = formatErrorMessage(`Failed to load network deployment ${contract.contractID}`, e); + throw e; + } + res["node_id"] = contract.nodeID; + for (const workload of res.workloads) { + const data = workload.data as NetworkLight; + if (workload.type !== WorkloadTypes.network || workload.name !== this.name) { + continue; + } + if (workload.result.state === "deleted") { + continue; + } + const znet_light = this._fromObj(data); + znet_light["node_id"] = contract.nodeID; + const reservedIps = await this.getReservedIps(contract.nodeID); + + if (znet_light.subnet !== this.ipRange) { + throw new ValidationError(`The same network name ${this.name} with a different ip range already exists.`); + } + this.node = { + contract_id: +contract.contractID, + node_id: contract.nodeID, + reserved_ips: reservedIps, + }; + this.network = znet_light; + this.deployments.push(res); + } + } + } private async getDeploymentContracts(name: string) { const contracts = await this.getMyNetworkContracts(true); @@ -264,59 +344,18 @@ class ZNetworkLight { return Array.from(new Set(contracts.map(c => c.parsedDeploymentData.name))); } - private async listNewNetworks() { + private async listAllNetworks() { const contracts = await this.getMyNetworkContracts(true); return this.getContractsName(contracts); } - private async existOnNewNetwork() { - return (await this.listNewNetworks()).includes(this.name); - } - - async getNetworkNames(): Promise { - const newNames = await this.listNewNetworks(); - - const path = this.getNetworksPath(); - const oldNames = await this.backendStorage.list(path); - return Array.from(new Set([...newNames, ...oldNames])); + private async exists() { + return (await this.listAllNetworks()).includes(this.name); } isPrivateIP(ip: string): boolean { return PrivateIp(ip.split("/")[0]); } - - async save(AddedContract?: Contract, deletedContract?: number) { - if (AddedContract?.contractType.nodeContract) - ZNetworkLight.newContracts.push({ - contractID: String(AddedContract.contractId), - createdAt: Date.now().toString(), - updatedAt: Date.now().toString(), - deploymentData: AddedContract.contractType.nodeContract.deploymentData, - deploymentHash: AddedContract.contractType.nodeContract.deploymentHash, - gridVersion: "4", - id: "", - nodeID: AddedContract.contractType.nodeContract.nodeId, - numberOfPublicIPs: AddedContract.contractType.nodeContract.publicIps, - solutionProviderID: String(AddedContract.solutionProviderId), - state: ContractStates.Created, - twinID: String(AddedContract.twinId), - parsedDeploymentData: JSON.parse(AddedContract.contractType.nodeContract.deploymentData), - resourcesUsed: undefined, - }); - - if (deletedContract) ZNetworkLight.deletedContracts.push(deletedContract); - - if (!this.node) { - await this.delete(); - } - } - - async delete(): Promise { - events.emit("logs", `Deleting network ${this.name}`); - const path = PATH.join(this.getNetworksPath(), this.name, "info.json"); - const updateOperations = await this.backendStorage.dump(path, ""); - await this.saveIfKVStoreBackend(updateOperations); - } } export { ZNetworkLight, Node };