From 4f8e9377463353e87026004d6405d190653d188d Mon Sep 17 00:00:00 2001 From: tangy5 Date: Mon, 19 Feb 2024 14:52:42 -0800 Subject: [PATCH 1/2] draft kratos service code Signed-off-by: tangy5 --- .../src/services/KratosService.js | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 plugins/ohif/extensions/monai-service/src/services/KratosService.js diff --git a/plugins/ohif/extensions/monai-service/src/services/KratosService.js b/plugins/ohif/extensions/monai-service/src/services/KratosService.js new file mode 100644 index 0000000..be8a5a4 --- /dev/null +++ b/plugins/ohif/extensions/monai-service/src/services/KratosService.js @@ -0,0 +1,177 @@ +/* +Copyright (c) 2021-2022, NVIDIA CORPORATION. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import axios from 'axios'; +import config from '../config.json'; +import { cache } from '@cornerstonejs/core'; + +export default class KratosServiceClient { + constructor() { + if (!window.config.monaiService) { + window.config.monaiService = { + server: config['MONAI_SERVICE_API_ENDPOINT'], + userId: config['MONAI_SERVICE_USER_ID'], + datasetId: config['MONAI_SERVICE_DATASET_ID'], + SSAToken: config['Kratos_SSA_TOKEN'] + } + } + + this.api_endpoint = window.config.monaiService.server; + this.user_id = window.config.monaiService.userId; + this.dataset_id = window.config.datasetId ? window.config.datasetId : window.config.monaiService.datasetId; + this.SSAToken = window.config.SSAToken; + this.base_url = `${this.api_endpoint}/users/${this.user_id}`; + this.cloudEvents_JSON = this._init_config_file() + console.log('this base url', this.base_url) + } + + _init_config_file() { + const kratos_config = { + "specversion": "1.0", + "id": "fe5302cf-1887-48a9-8e6e-117d366a7344", + "time": "2022-06-01T12:30:00.123456Z", + "source": "service_zone-name_machine-uuid", + "type": "some-event-type", + "subject": "some-event-subject", + "data": { + "StringField1": "abc", + "StringField2": "def", + "IntegerField3": 1, + "FloatField4": 3.3, + "NestedField5": { + "NestedStringField1": "xyz", + "NestedIntegerField2": 2 + }, + "StringField6": "ghi", + "IPField7": "10.1.2.3", + "TimestampField8": "2022-06-01T12:30:00.123Z" + } + } + + return kratos_config + } + + + async fetch_SSA_token(clientId, clientSecret) { + const credentials = `${clientId}:${clientSecret}`; + const base64Credentials = Buffer.from(credentials).toString('base64') + + const url = 'https://hvgfdxorrc6-em5mhrg7o3lr5skadstwarphmyuy4lg.ssa.nvidia.com/token'; + const postData = 'grant_type=client_credentials&scope=telemetry-write'; + + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': `Basic ${base64Credentials}` + }, + body: postData + }; + + fetch(url, options) + .then(response => response.json()) + .then(data => console.log(data)) + .catch(error => console.error('Error:', error)); + } + + async send_telemetry(eventData) { + const url = 'https://prod.analytics.nvidiagrid.net/api/v2/topic/your-kratos-collector-id'; + + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/cloudevents+json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(eventData) + }; + + try { + const response = await fetch(url, options); + const responseData = await response.json(); + console.log('Response:', responseData); + } catch (error) { + console.error('Error:', error); + } + } + + async update_telemetry(telemetryFile) { + const url = 'https://prod.analytics.nvidiagrid.net/api/v2/topic/your-kratos-collector-id'; + + function writeToFileSync() { + this._ignore_event_config = true; + fs.writeFileSync(this._datastore_config_path, JSON.stringify(this._datastore, (key, value) => key === "base_path" ? undefined : value, 2)); + this._config_ts = fs.statSync(this._datastore_config_path).mtime; + console.log("+++ Telemetry is updated..."); + } + writeToFileSync.call(this); + } + + async status() { + const url = 'https://prod.analytics.nvidiagrid.net/api/v2/topic/your-kratos-collector-id'; + const dict = {} + return { + "completed":{} + } + } + + api_get(url) { + console.debug('GET:: ' + url); + if (this.accessToken) { + axios.defaults.headers.common['Authorization'] = this.accessToken; + } + + return axios.get(url,{ + headers: { + 'Authorization': this.accessToken ? `Bearer ${this.accessToken}` : undefined, + }, + verify: false + }) + .then(function(response) { + console.debug(response); + return response; + }) + .catch(function(error) { + return error; + }) + .finally(function() { + }); + } + + api_post(url, body, responseType = 'json') { + console.log('POST URL', url) + if (this.accessToken) { + axios.defaults.headers.common['Authorization'] = this.accessToken; + } + + console.debug('POST:: ' + url); + return axios.post(url, body, { + responseType: responseType, + headers: { + accept: ['application/json', 'multipart/form-data'], + }, + verify:false + }).then(function(response) { + console.debug(response); + console.log(response) + return response; + }).catch(function(error) { + console.log(error) + return error; + }).finally(function() { + }); + } +} From e810adf18b1d1a664176c43a41be2979e701b793 Mon Sep 17 00:00:00 2001 From: tangy5 Date: Sun, 3 Mar 2024 13:24:29 -0800 Subject: [PATCH 2/2] update TAO based telemetry server and requests --- .../src/components/MonaiServicePanel.tsx | 9 + .../src/services/KratosService.js | 177 ------------------ .../src/services/KratosServiceClient.js | 158 ++++++++++++++++ 3 files changed, 167 insertions(+), 177 deletions(-) delete mode 100644 plugins/ohif/extensions/monai-service/src/services/KratosService.js create mode 100644 plugins/ohif/extensions/monai-service/src/services/KratosServiceClient.js diff --git a/plugins/ohif/extensions/monai-service/src/components/MonaiServicePanel.tsx b/plugins/ohif/extensions/monai-service/src/components/MonaiServicePanel.tsx index b6f4ec7..5520061 100644 --- a/plugins/ohif/extensions/monai-service/src/components/MonaiServicePanel.tsx +++ b/plugins/ohif/extensions/monai-service/src/components/MonaiServicePanel.tsx @@ -22,6 +22,8 @@ import AutoSegmentation from './actions/AutoSegmentation'; import PointPrompts from './actions/PointPrompts'; import ClassPrompts from './actions/ClassPrompts'; import MonaiServiceClient from '../services/MonaiServiceClient'; +import KratosServiceClient from '../services/KratosServiceClient'; + import { hideNotification, getLabelColor } from '../utils/GenericUtils'; import { Enums } from '@cornerstonejs/tools'; import { cache, triggerEvent, eventTarget } from '@cornerstonejs/core'; @@ -100,6 +102,10 @@ export default class MonaiServicePanel extends Component { return c; }; + kratos_client = () => { + const c = new KratosServiceClient(); + return c; + }; segmentColor(label) { const color = getLabelColor(label); @@ -121,6 +127,9 @@ export default class MonaiServicePanel extends Component { // const response_0 = await this.client().get_token(); // console.log(response_0.data) + // setup kratos client + const kratos_res = await this.kratos_client().sendTelemetryData("detectnet_v2", "train", [{ name: "gpu_data" }], 1); + ; const response = await this.client().list_models(); console.log('list models:', response) diff --git a/plugins/ohif/extensions/monai-service/src/services/KratosService.js b/plugins/ohif/extensions/monai-service/src/services/KratosService.js deleted file mode 100644 index be8a5a4..0000000 --- a/plugins/ohif/extensions/monai-service/src/services/KratosService.js +++ /dev/null @@ -1,177 +0,0 @@ -/* -Copyright (c) 2021-2022, NVIDIA CORPORATION. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import axios from 'axios'; -import config from '../config.json'; -import { cache } from '@cornerstonejs/core'; - -export default class KratosServiceClient { - constructor() { - if (!window.config.monaiService) { - window.config.monaiService = { - server: config['MONAI_SERVICE_API_ENDPOINT'], - userId: config['MONAI_SERVICE_USER_ID'], - datasetId: config['MONAI_SERVICE_DATASET_ID'], - SSAToken: config['Kratos_SSA_TOKEN'] - } - } - - this.api_endpoint = window.config.monaiService.server; - this.user_id = window.config.monaiService.userId; - this.dataset_id = window.config.datasetId ? window.config.datasetId : window.config.monaiService.datasetId; - this.SSAToken = window.config.SSAToken; - this.base_url = `${this.api_endpoint}/users/${this.user_id}`; - this.cloudEvents_JSON = this._init_config_file() - console.log('this base url', this.base_url) - } - - _init_config_file() { - const kratos_config = { - "specversion": "1.0", - "id": "fe5302cf-1887-48a9-8e6e-117d366a7344", - "time": "2022-06-01T12:30:00.123456Z", - "source": "service_zone-name_machine-uuid", - "type": "some-event-type", - "subject": "some-event-subject", - "data": { - "StringField1": "abc", - "StringField2": "def", - "IntegerField3": 1, - "FloatField4": 3.3, - "NestedField5": { - "NestedStringField1": "xyz", - "NestedIntegerField2": 2 - }, - "StringField6": "ghi", - "IPField7": "10.1.2.3", - "TimestampField8": "2022-06-01T12:30:00.123Z" - } - } - - return kratos_config - } - - - async fetch_SSA_token(clientId, clientSecret) { - const credentials = `${clientId}:${clientSecret}`; - const base64Credentials = Buffer.from(credentials).toString('base64') - - const url = 'https://hvgfdxorrc6-em5mhrg7o3lr5skadstwarphmyuy4lg.ssa.nvidia.com/token'; - const postData = 'grant_type=client_credentials&scope=telemetry-write'; - - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': `Basic ${base64Credentials}` - }, - body: postData - }; - - fetch(url, options) - .then(response => response.json()) - .then(data => console.log(data)) - .catch(error => console.error('Error:', error)); - } - - async send_telemetry(eventData) { - const url = 'https://prod.analytics.nvidiagrid.net/api/v2/topic/your-kratos-collector-id'; - - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/cloudevents+json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify(eventData) - }; - - try { - const response = await fetch(url, options); - const responseData = await response.json(); - console.log('Response:', responseData); - } catch (error) { - console.error('Error:', error); - } - } - - async update_telemetry(telemetryFile) { - const url = 'https://prod.analytics.nvidiagrid.net/api/v2/topic/your-kratos-collector-id'; - - function writeToFileSync() { - this._ignore_event_config = true; - fs.writeFileSync(this._datastore_config_path, JSON.stringify(this._datastore, (key, value) => key === "base_path" ? undefined : value, 2)); - this._config_ts = fs.statSync(this._datastore_config_path).mtime; - console.log("+++ Telemetry is updated..."); - } - writeToFileSync.call(this); - } - - async status() { - const url = 'https://prod.analytics.nvidiagrid.net/api/v2/topic/your-kratos-collector-id'; - const dict = {} - return { - "completed":{} - } - } - - api_get(url) { - console.debug('GET:: ' + url); - if (this.accessToken) { - axios.defaults.headers.common['Authorization'] = this.accessToken; - } - - return axios.get(url,{ - headers: { - 'Authorization': this.accessToken ? `Bearer ${this.accessToken}` : undefined, - }, - verify: false - }) - .then(function(response) { - console.debug(response); - return response; - }) - .catch(function(error) { - return error; - }) - .finally(function() { - }); - } - - api_post(url, body, responseType = 'json') { - console.log('POST URL', url) - if (this.accessToken) { - axios.defaults.headers.common['Authorization'] = this.accessToken; - } - - console.debug('POST:: ' + url); - return axios.post(url, body, { - responseType: responseType, - headers: { - accept: ['application/json', 'multipart/form-data'], - }, - verify:false - }).then(function(response) { - console.debug(response); - console.log(response) - return response; - }).catch(function(error) { - console.log(error) - return error; - }).finally(function() { - }); - } -} diff --git a/plugins/ohif/extensions/monai-service/src/services/KratosServiceClient.js b/plugins/ohif/extensions/monai-service/src/services/KratosServiceClient.js new file mode 100644 index 0000000..629daaf --- /dev/null +++ b/plugins/ohif/extensions/monai-service/src/services/KratosServiceClient.js @@ -0,0 +1,158 @@ +/* +Copyright (c) 2021-2022, NVIDIA CORPORATION. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import axios from 'axios'; +import config from '../config.json'; +import { cache } from '@cornerstonejs/core'; + +export default class KratosServiceClient { + constructor() { + if (!window.config.monaiService) { + window.config.monaiService = { + server: config['MONAI_SERVICE_API_ENDPOINT'], + userId: config['MONAI_SERVICE_USER_ID'], + datasetId: config['MONAI_SERVICE_DATASET_ID'], + SSAToken: config['Kratos_SSA_TOKEN'] + } + require('dotenv').config(); + + this.axios = require('axios'); + this.fs = require('fs'); + this.https = require('https'); + const { exec } = require('child_process'); + this.os = require('os'); + this.path = require('path'); + + //TODO: set SERVER IP and TELEMETRY Server in NGINX conf + this.TAO_CERTIFICATES_URL = "https://gitlab-master.nvidia.com/vpraveen/python_wheels/-/raw/main/certs/certificates.tar.gz"; + this.TAO_SERVER_IP = "10.111.60.42"; + this.TAO_TELEMETRY_SERVER = "https://tao-telemetry.nvidia.com:9443/api/v1/telemetry" + + } + + console.log('this base url', this.base_url) + } + + + urlExists(url) { + return new Promise((resolve, reject) => { + https.get(url, { method: 'HEAD' }, (res) => { + resolve(res.statusCode === 200); + }).on('error', (err) => { + resolve(false); + }); + }); + } + + async getCertificates() { + const certificatesUrl = process.env.TAO_CERTIFICATES_URL || TAO_CERTIFICATES_URL; + if (!await urlExists(certificatesUrl)) { + throw new Error("Url for the certificates not found."); + } + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'certs-')); + const downloadCommand = `wget ${certificatesUrl} -P ${tmpDir} --quiet`; + return new Promise((resolve, reject) => { + exec(downloadCommand, (error, stdout, stderr) => { + if (error) { + reject(new Error("Download certificates.tar.gz failed.")); + return; + } + const tarfilePath = path.join(tmpDir, "certificates.tar.gz"); + resolve(tmpDir); + }); + }); + } + + async sendTelemetryData(network, action, gpuData, numGpus = 1, timeLapsed = null) { + if (process.env.TELEMETRY_OPT_OUT?.toLowerCase() === "no" || process.env.TELEMETRY_OPT_OUT?.toLowerCase() === "false" || process.env.TELEMETRY_OPT_OUT === "0") { + const url = this.TAO_TELEMETRY_SERVER; + const data = { + version: process.env.TAO_TOOLKIT_VERSION || "4.22.11", + action: action, + network: network, + gpu: gpuData.slice(0, numGpus).map(device => device.name), + gpu_details: gpuData.slice(0, numGpus) + }; + if (timeLapsed !== null) { + data.time_lapsed = timeLapsed; + } + try { + const certificateDir = await getCertificates(); + const cert = ['client-cert.pem', 'client-key.pem'].map(item => path.join(certificateDir, item)); + const verify = path.join(certificateDir, 'ca-cert.pem'); + await axios.post(url, data, { + httpsAgent: new https.Agent({ + cert: fs.readFileSync(cert[0]), + key: fs.readFileSync(cert[1]), + ca: fs.readFileSync(verify) + }) + }); + console.log(`Telemetry data posted: \n${JSON.stringify(data, null, 4)}`); + } catch (error) { + console.error(error); + } + } + } + + + + api_get(url) { + console.debug('GET:: ' + url); + if (this.accessToken) { + axios.defaults.headers.common['Authorization'] = this.accessToken; + } + + return axios.get(url,{ + headers: { + 'Authorization': this.accessToken ? `Bearer ${this.accessToken}` : undefined, + }, + verify: false + }) + .then(function(response) { + console.debug(response); + return response; + }) + .catch(function(error) { + return error; + }) + .finally(function() { + }); + } + + api_post(url, body, responseType = 'json') { + console.log('POST URL', url) + if (this.accessToken) { + axios.defaults.headers.common['Authorization'] = this.accessToken; + } + + console.debug('POST:: ' + url); + return axios.post(url, body, { + responseType: responseType, + headers: { + accept: ['application/json', 'multipart/form-data'], + }, + verify:false + }).then(function(response) { + console.debug(response); + console.log(response) + return response; + }).catch(function(error) { + console.log(error) + return error; + }).finally(function() { + }); + } +}