From a012a44b3be1eadb18db0a8290a17e3be9f02f18 Mon Sep 17 00:00:00 2001 From: liuzhe Date: Tue, 25 May 2021 14:47:14 +0800 Subject: [PATCH 1/6] fix 3rd-part training service bugs --- nni/tools/nnictl/ts_management.py | 4 +- nni/tools/trial_tool/trial_runner.py | 3 + .../common/experimentStartupInfo.ts | 169 ++++++++---------- ts/nni_manager/common/pythonScript.ts | 25 +-- ts/nni_manager/common/utils.ts | 4 +- .../environments/amlEnvironmentService.ts | 7 +- .../environments/environmentServiceFactory.ts | 18 +- .../environments/localEnvironmentService.ts | 7 +- .../environments/openPaiEnvironmentService.ts | 5 +- .../environments/remoteEnvironmentService.ts | 7 +- 10 files changed, 118 insertions(+), 131 deletions(-) diff --git a/nni/tools/nnictl/ts_management.py b/nni/tools/nnictl/ts_management.py index 79d6e5b25d..151b053591 100644 --- a/nni/tools/nnictl/ts_management.py +++ b/nni/tools/nnictl/ts_management.py @@ -39,8 +39,8 @@ def register(args): try: service_config = { - 'node_module_path': info.node_module_path, - 'node_class_name': info.node_class_name, + 'nodeModulePath': str(info.node_module_path), + 'nodeClassName': info.node_class_name, } json.dumps(service_config) except Exception: diff --git a/nni/tools/trial_tool/trial_runner.py b/nni/tools/trial_tool/trial_runner.py index ebc6ee7dad..1b180d04c0 100644 --- a/nni/tools/trial_tool/trial_runner.py +++ b/nni/tools/trial_tool/trial_runner.py @@ -148,6 +148,9 @@ def check_version(args): PARSER.add_argument('--job_pid_file', type=str, help='save trial runner process pid') args, unknown = PARSER.parse_known_args() + import os + print('cwd:', os.getcwd()) + setting_file = "settings.json" if not os.path.exists(setting_file): setting_file = "../{}".format(setting_file) diff --git a/ts/nni_manager/common/experimentStartupInfo.ts b/ts/nni_manager/common/experimentStartupInfo.ts index c29d103041..b4c45c8f0f 100644 --- a/ts/nni_manager/common/experimentStartupInfo.ts +++ b/ts/nni_manager/common/experimentStartupInfo.ts @@ -6,36 +6,41 @@ import * as assert from 'assert'; import * as os from 'os'; import * as path from 'path'; -import * as component from '../common/component'; - -@component.Singleton -class ExperimentStartupInfo { - private readonly API_ROOT_URL: string = '/api/v1/nni'; - - private experimentId: string = ''; - private newExperiment: boolean = true; - private basePort: number = -1; - private initialized: boolean = false; - private logDir: string = ''; - private logLevel: string = ''; - private readonly: boolean = false; - private dispatcherPipe: string | null = null; - private platform: string = ''; - private urlprefix: string = ''; - - public setStartupInfo(newExperiment: boolean, experimentId: string, basePort: number, platform: string, logDir?: string, logLevel?: string, readonly?: boolean, dispatcherPipe?: string, urlprefix?: string): void { - assert(!this.initialized); - assert(experimentId.trim().length > 0); + +const API_ROOT_URL: string = '/api/v1/nni'; + +export class ExperimentStartupInfo { + + public experimentId: string = ''; + public newExperiment: boolean = true; + public basePort: number = -1; + public initialized: boolean = false; + public logDir: string = ''; + public logLevel: string = ''; + public readonly: boolean = false; + public dispatcherPipe: string | null = null; + public platform: string = ''; + public urlprefix: string = ''; + + constructor( + newExperiment: boolean, + experimentId: string, + basePort: number, + platform: string, + logDir?: string, + logLevel?: string, + readonly?: boolean, + dispatcherPipe?: string, + urlprefix?: string) { this.newExperiment = newExperiment; this.experimentId = experimentId; this.basePort = basePort; - this.initialized = true; this.platform = platform; if (logDir !== undefined && logDir.length > 0) { - this.logDir = path.join(path.normalize(logDir), this.getExperimentId()); + this.logDir = path.join(path.normalize(logDir), experimentId); } else { - this.logDir = path.join(os.homedir(), 'nni-experiments', this.getExperimentId()); + this.logDir = path.join(os.homedir(), 'nni-experiments', experimentId); } if (logLevel !== undefined && logLevel.length > 1) { @@ -55,98 +60,70 @@ class ExperimentStartupInfo { } } - public getExperimentId(): string { - assert(this.initialized); - - return this.experimentId; - } - - public getBasePort(): number { - assert(this.initialized); - - return this.basePort; - } - - public isNewExperiment(): boolean { - assert(this.initialized); - - return this.newExperiment; - } - - public getPlatform(): string { - assert(this.initialized); - - return this.platform; - } - - public getLogDir(): string { - assert(this.initialized); - - return this.logDir; - } - - public getLogLevel(): string { - assert(this.initialized); - - return this.logLevel; - } - - public isReadonly(): boolean { - assert(this.initialized); - - return this.readonly; - } - - public getDispatcherPipe(): string | null { - assert(this.initialized); - return this.dispatcherPipe; + public get apiRootUrl(): string { + return this.urlprefix === '' ? API_ROOT_URL : `/${this.urlprefix}${API_ROOT_URL}`; } - public getAPIRootUrl(): string { - assert(this.initialized); - return this.urlprefix==''?this.API_ROOT_URL:`/${this.urlprefix}${this.API_ROOT_URL}`; + public static getInstance(): ExperimentStartupInfo { + assert(singleton !== null); + return singleton!; } } -function getExperimentId(): string { - return component.get(ExperimentStartupInfo).getExperimentId(); -} +let singleton: ExperimentStartupInfo | null = null; -function getBasePort(): number { - return component.get(ExperimentStartupInfo).getBasePort(); +export function getExperimentStartupInfo(): ExperimentStartupInfo { + return ExperimentStartupInfo.getInstance(); } -function isNewExperiment(): boolean { - return component.get(ExperimentStartupInfo).isNewExperiment(); +export function setExperimentStartupInfo( + newExperiment: boolean, + experimentId: string, + basePort: number, + platform: string, + logDir?: string, + logLevel?: string, + readonly?: boolean, + dispatcherPipe?: string, + urlprefix?: string): void { + assert(singleton === null); + singleton = new ExperimentStartupInfo( + newExperiment, + experimentId, + basePort, + platform, + logDir, + logLevel, + readonly, + dispatcherPipe, + urlprefix + ); } -function getPlatform(): string { - return component.get(ExperimentStartupInfo).getPlatform(); +export function getExperimentId(): string { + return getExperimentStartupInfo().experimentId; } -function getExperimentStartupInfo(): ExperimentStartupInfo { - return component.get(ExperimentStartupInfo); +export function getBasePort(): number { + return getExperimentStartupInfo().basePort; } -function setExperimentStartupInfo( - newExperiment: boolean, experimentId: string, basePort: number, platform: string, logDir?: string, logLevel?: string, readonly?: boolean, dispatcherPipe?: string, urlprefix?: string): void { - component.get(ExperimentStartupInfo) - .setStartupInfo(newExperiment, experimentId, basePort, platform, logDir, logLevel, readonly, dispatcherPipe, urlprefix); +export function isNewExperiment(): boolean { + return getExperimentStartupInfo().newExperiment; } -function isReadonly(): boolean { - return component.get(ExperimentStartupInfo).isReadonly(); +export function getPlatform(): string { + return getExperimentStartupInfo().platform; } -function getDispatcherPipe(): string | null { - return component.get(ExperimentStartupInfo).getDispatcherPipe(); +export function isReadonly(): boolean { + return getExperimentStartupInfo().readonly; } -function getAPIRootUrl(): string { - return component.get(ExperimentStartupInfo).getAPIRootUrl(); +export function getDispatcherPipe(): string | null { + return getExperimentStartupInfo().dispatcherPipe; } -export { - ExperimentStartupInfo, getBasePort, getExperimentId, isNewExperiment, getPlatform, getExperimentStartupInfo, - setExperimentStartupInfo, isReadonly, getDispatcherPipe, getAPIRootUrl -}; +export function getAPIRootUrl(): string { + return getExperimentStartupInfo().apiRootUrl; +} diff --git a/ts/nni_manager/common/pythonScript.ts b/ts/nni_manager/common/pythonScript.ts index d3a2774204..12a2bf2112 100644 --- a/ts/nni_manager/common/pythonScript.ts +++ b/ts/nni_manager/common/pythonScript.ts @@ -6,27 +6,32 @@ import { spawn } from 'child_process'; import { Logger, getLogger } from './log'; -const python = process.platform === 'win32' ? 'python.exe' : 'python3'; +const logger: Logger = getLogger('pythonScript'); -export async function runPythonScript(script: string, logger?: Logger): Promise { +const python: string = process.platform === 'win32' ? 'python.exe' : 'python3'; + +export async function runPythonScript(script: string, logTag?: string): Promise { const proc = spawn(python, [ '-c', script ]); + let stdout: string = ''; + let stderr: string = ''; + proc.stdout.on('data', (data: string) => { stdout += data; }); + proc.stderr.on('data', (data: string) => { stderr += data; }); + const procPromise = new Promise((resolve, reject) => { proc.on('error', (err: Error) => { reject(err); }); proc.on('exit', () => { resolve(); }); }); await procPromise; - const stdout = proc.stdout.read().toString(); - const stderr = proc.stderr.read().toString(); - if (stderr) { - if (logger === undefined) { - logger = getLogger('pythonScript'); + if (logTag) { + logger.warning(`Python script [${logTag}] has stderr:`, stderr); + } else { + logger.warning('Python script has stderr.'); + logger.warning(' script:', script); + logger.warning(' stderr:', stderr); } - logger.warning('python script has stderr.'); - logger.warning('script:', script); - logger.warning('stderr:', stderr); } return stdout; diff --git a/ts/nni_manager/common/utils.ts b/ts/nni_manager/common/utils.ts index 02ab07ca0a..451439139a 100644 --- a/ts/nni_manager/common/utils.ts +++ b/ts/nni_manager/common/utils.ts @@ -25,7 +25,7 @@ import { ExperimentManager } from './experimentManager'; import { HyperParameters, TrainingService, TrialJobStatus } from './trainingService'; function getExperimentRootDir(): string { - return getExperimentStartupInfo().getLogDir(); + return getExperimentStartupInfo().logDir; } function getLogDir(): string { @@ -33,7 +33,7 @@ function getLogDir(): string { } function getLogLevel(): string { - return getExperimentStartupInfo().getLogLevel(); + return getExperimentStartupInfo().logLevel; } function getDefaultDatabaseDir(): string { diff --git a/ts/nni_manager/training_service/reusable/environments/amlEnvironmentService.ts b/ts/nni_manager/training_service/reusable/environments/amlEnvironmentService.ts index 48a65db1f8..58d25e144e 100644 --- a/ts/nni_manager/training_service/reusable/environments/amlEnvironmentService.ts +++ b/ts/nni_manager/training_service/reusable/environments/amlEnvironmentService.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import * as component from '../../../common/component'; import { getLogger, Logger } from '../../../common/log'; import { ExperimentConfig, AmlConfig, flattenConfig } from '../../../common/experimentConfig'; +import { ExperimentStartupInfo } from '../../../common/experimentStartupInfo'; import { validateCodeDir } from '../../common/util'; import { AMLClient } from '../aml/amlClient'; import { AMLEnvironmentInformation } from '../aml/amlConfig'; @@ -29,10 +30,10 @@ export class AMLEnvironmentService extends EnvironmentService { private experimentRootDir: string; private config: FlattenAmlConfig; - constructor(experimentRootDir: string, experimentId: string, config: ExperimentConfig) { + constructor(config: ExperimentConfig, info: ExperimentStartupInfo) { super(); - this.experimentId = experimentId; - this.experimentRootDir = experimentRootDir; + this.experimentId = info.experimentId; + this.experimentRootDir = info.logDir; this.config = flattenConfig(config, 'aml'); validateCodeDir(this.config.trialCodeDirectory); } diff --git a/ts/nni_manager/training_service/reusable/environments/environmentServiceFactory.ts b/ts/nni_manager/training_service/reusable/environments/environmentServiceFactory.ts index 62f5725b04..24d7b8c088 100644 --- a/ts/nni_manager/training_service/reusable/environments/environmentServiceFactory.ts +++ b/ts/nni_manager/training_service/reusable/environments/environmentServiceFactory.ts @@ -4,24 +4,22 @@ import { LocalEnvironmentService } from './localEnvironmentService'; import { RemoteEnvironmentService } from './remoteEnvironmentService'; import { EnvironmentService } from '../environment'; import { ExperimentConfig } from '../../../common/experimentConfig'; -import { getExperimentId } from '../../../common/experimentStartupInfo'; +import { ExperimentStartupInfo } from '../../../common/experimentStartupInfo'; import { getCustomEnvironmentServiceConfig } from '../../../common/nniConfig'; -import { getExperimentRootDir, importModule } from '../../../common/utils'; - +import { importModule } from '../../../common/utils'; export async function createEnvironmentService(name: string, config: ExperimentConfig): Promise { - const expId = getExperimentId(); - const rootDir = getExperimentRootDir(); + const info = ExperimentStartupInfo.getInstance(); switch(name) { case 'local': - return new LocalEnvironmentService(rootDir, expId, config); + return new LocalEnvironmentService(config, info); case 'remote': - return new RemoteEnvironmentService(rootDir, expId, config); + return new RemoteEnvironmentService(config, info); case 'aml': - return new AMLEnvironmentService(rootDir, expId, config); + return new AMLEnvironmentService(config, info); case 'openpai': - return new OpenPaiEnvironmentService(rootDir, expId, config); + return new OpenPaiEnvironmentService(config, info); } const esConfig = await getCustomEnvironmentServiceConfig(name); @@ -30,5 +28,5 @@ export async function createEnvironmentService(name: string, config: ExperimentC } const esModule = importModule(esConfig.nodeModulePath); const esClass = esModule[esConfig.nodeClassName] as any; - return new esClass(rootDir, expId, config); + return new esClass(config, info); } diff --git a/ts/nni_manager/training_service/reusable/environments/localEnvironmentService.ts b/ts/nni_manager/training_service/reusable/environments/localEnvironmentService.ts index b462840168..f75a14d805 100644 --- a/ts/nni_manager/training_service/reusable/environments/localEnvironmentService.ts +++ b/ts/nni_manager/training_service/reusable/environments/localEnvironmentService.ts @@ -9,6 +9,7 @@ import * as tkill from 'tree-kill'; import * as component from '../../../common/component'; import { getLogger, Logger } from '../../../common/log'; import { ExperimentConfig } from '../../../common/experimentConfig'; +import { ExperimentStartupInfo } from '../../../common/experimentStartupInfo'; import { EnvironmentInformation, EnvironmentService } from '../environment'; import { isAlive, getNewLine } from '../../../common/utils'; import { execMkdir, runScript, getScriptName, execCopydir } from '../../common/util'; @@ -21,10 +22,10 @@ export class LocalEnvironmentService extends EnvironmentService { private experimentRootDir: string; private experimentId: string; - constructor(experimentRootDir: string, experimentId: string, _config: ExperimentConfig) { + constructor(_config: ExperimentConfig, info: ExperimentStartupInfo) { super(); - this.experimentId = experimentId; - this.experimentRootDir = experimentRootDir; + this.experimentId = info.experimentId; + this.experimentRootDir = info.logDir; } public get environmentMaintenceLoopInterval(): number { diff --git a/ts/nni_manager/training_service/reusable/environments/openPaiEnvironmentService.ts b/ts/nni_manager/training_service/reusable/environments/openPaiEnvironmentService.ts index 0ebe482aa2..6345ca6f95 100644 --- a/ts/nni_manager/training_service/reusable/environments/openPaiEnvironmentService.ts +++ b/ts/nni_manager/training_service/reusable/environments/openPaiEnvironmentService.ts @@ -8,6 +8,7 @@ import * as request from 'request'; import { Deferred } from 'ts-deferred'; import * as component from '../../../common/component'; import { ExperimentConfig, OpenpaiConfig, flattenConfig, toMegaBytes } from '../../../common/experimentConfig'; +import { ExperimentStartupInfo } from '../../../common/experimentStartupInfo'; import { getLogger, Logger } from '../../../common/log'; import { PAIClusterConfig } from '../../pai/paiConfig'; import { NNIPAITrialConfig } from '../../pai/paiConfig'; @@ -31,9 +32,9 @@ export class OpenPaiEnvironmentService extends EnvironmentService { private experimentId: string; private config: FlattenOpenpaiConfig; - constructor(_experimentRootDir: string, experimentId: string, config: ExperimentConfig) { + constructor(config: ExperimentConfig, info: ExperimentStartupInfo) { super(); - this.experimentId = experimentId; + this.experimentId = info.experimentId; this.config = flattenConfig(config, 'openpai'); this.paiToken = this.config.token; this.protocol = this.config.host.toLowerCase().startsWith('https://') ? 'https' : 'http'; diff --git a/ts/nni_manager/training_service/reusable/environments/remoteEnvironmentService.ts b/ts/nni_manager/training_service/reusable/environments/remoteEnvironmentService.ts index 3f5fed8e5c..5add571275 100644 --- a/ts/nni_manager/training_service/reusable/environments/remoteEnvironmentService.ts +++ b/ts/nni_manager/training_service/reusable/environments/remoteEnvironmentService.ts @@ -10,6 +10,7 @@ import { getLogger, Logger } from '../../../common/log'; import { EnvironmentInformation, EnvironmentService } from '../environment'; import { getLogLevel } from '../../../common/utils'; import { ExperimentConfig, RemoteConfig, RemoteMachineConfig, flattenConfig } from '../../../common/experimentConfig'; +import { ExperimentStartupInfo } from '../../../common/experimentStartupInfo'; import { execMkdir } from '../../common/util'; import { ExecutorManager } from '../../remote_machine/remoteMachineData'; import { ShellExecutor } from 'training_service/remote_machine/shellExecutor'; @@ -32,13 +33,13 @@ export class RemoteEnvironmentService extends EnvironmentService { private experimentId: string; private config: FlattenRemoteConfig; - constructor(experimentRootDir: string, experimentId: string, config: ExperimentConfig) { + constructor(config: ExperimentConfig, info: ExperimentStartupInfo) { super(); - this.experimentId = experimentId; + this.experimentId = info.experimentId; this.environmentExecutorManagerMap = new Map(); this.machineExecutorManagerMap = new Map(); this.remoteMachineMetaOccupiedMap = new Map(); - this.experimentRootDir = experimentRootDir; + this.experimentRootDir = info.logDir; this.log = getLogger('RemoteEnvironmentService'); this.config = flattenConfig(config, 'remote'); From 9b324527dcf44ead21c32677dff7edfbe5702771 Mon Sep 17 00:00:00 2001 From: liuzhe Date: Tue, 25 May 2021 14:48:17 +0800 Subject: [PATCH 2/6] remove debug info --- nni/tools/trial_tool/trial_runner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/nni/tools/trial_tool/trial_runner.py b/nni/tools/trial_tool/trial_runner.py index 1b180d04c0..ebc6ee7dad 100644 --- a/nni/tools/trial_tool/trial_runner.py +++ b/nni/tools/trial_tool/trial_runner.py @@ -148,9 +148,6 @@ def check_version(args): PARSER.add_argument('--job_pid_file', type=str, help='save trial runner process pid') args, unknown = PARSER.parse_known_args() - import os - print('cwd:', os.getcwd()) - setting_file = "settings.json" if not os.path.exists(setting_file): setting_file = "../{}".format(setting_file) From 2714545cf5bc6403b1b849391a3e951bc97210c2 Mon Sep 17 00:00:00 2001 From: liuzhe Date: Fri, 4 Jun 2021 14:53:26 +0800 Subject: [PATCH 3/6] fix lint --- ts/nni_manager/common/experimentStartupInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/nni_manager/common/experimentStartupInfo.ts b/ts/nni_manager/common/experimentStartupInfo.ts index b4c45c8f0f..7c2f4f644d 100644 --- a/ts/nni_manager/common/experimentStartupInfo.ts +++ b/ts/nni_manager/common/experimentStartupInfo.ts @@ -9,6 +9,8 @@ import * as path from 'path'; const API_ROOT_URL: string = '/api/v1/nni'; +let singleton: ExperimentStartupInfo | null = null; + export class ExperimentStartupInfo { public experimentId: string = ''; @@ -70,8 +72,6 @@ export class ExperimentStartupInfo { } } -let singleton: ExperimentStartupInfo | null = null; - export function getExperimentStartupInfo(): ExperimentStartupInfo { return ExperimentStartupInfo.getInstance(); } From 325b1fc34a11c22ee218736319865947a190e9a6 Mon Sep 17 00:00:00 2001 From: liuzhe Date: Fri, 4 Jun 2021 15:41:08 +0800 Subject: [PATCH 4/6] try fix ut --- ts/nni_manager/common/experimentStartupInfo.ts | 1 - ts/nni_manager/common/utils.ts | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ts/nni_manager/common/experimentStartupInfo.ts b/ts/nni_manager/common/experimentStartupInfo.ts index 7c2f4f644d..ee3e80584e 100644 --- a/ts/nni_manager/common/experimentStartupInfo.ts +++ b/ts/nni_manager/common/experimentStartupInfo.ts @@ -86,7 +86,6 @@ export function setExperimentStartupInfo( readonly?: boolean, dispatcherPipe?: string, urlprefix?: string): void { - assert(singleton === null); singleton = new ExperimentStartupInfo( newExperiment, experimentId, diff --git a/ts/nni_manager/common/utils.ts b/ts/nni_manager/common/utils.ts index 451439139a..87fda292ad 100644 --- a/ts/nni_manager/common/utils.ts +++ b/ts/nni_manager/common/utils.ts @@ -184,7 +184,6 @@ function generateParamFileName(hyperParameters: HyperParameters): string { * Must be paired with `cleanupUnitTest()`. */ function prepareUnitTest(): void { - Container.snapshot(ExperimentStartupInfo); Container.snapshot(Database); Container.snapshot(DataStore); Container.snapshot(TrainingService); @@ -213,7 +212,7 @@ function cleanupUnitTest(): void { Container.restore(TrainingService); Container.restore(DataStore); Container.restore(Database); - Container.restore(ExperimentStartupInfo); + setExperimentStartupInfo(true, 'unittest', 8080, 'unittest', undefined, logLevel); Container.restore(ExperimentManager); } From 17c24ab1897e8b26e424471d6d7db1f84b2619cc Mon Sep 17 00:00:00 2001 From: liuzhe Date: Tue, 25 May 2021 16:23:44 +0800 Subject: [PATCH 5/6] fix lint --- ts/nni_manager/common/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/nni_manager/common/utils.ts b/ts/nni_manager/common/utils.ts index 87fda292ad..367d462607 100644 --- a/ts/nni_manager/common/utils.ts +++ b/ts/nni_manager/common/utils.ts @@ -19,7 +19,7 @@ import * as util from 'util'; import * as glob from 'glob'; import { Database, DataStore } from './datastore'; -import { ExperimentStartupInfo, getExperimentStartupInfo, setExperimentStartupInfo } from './experimentStartupInfo'; +import { getExperimentStartupInfo, setExperimentStartupInfo } from './experimentStartupInfo'; import { ExperimentConfig, Manager } from './manager'; import { ExperimentManager } from './experimentManager'; import { HyperParameters, TrainingService, TrialJobStatus } from './trainingService'; From 345fd922c9ce1b3ff09e99b634c685e4a9e4a2b7 Mon Sep 17 00:00:00 2001 From: liuzhe Date: Tue, 25 May 2021 16:41:42 +0800 Subject: [PATCH 6/6] bugfix --- ts/nni_manager/common/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts/nni_manager/common/utils.ts b/ts/nni_manager/common/utils.ts index 367d462607..011f187862 100644 --- a/ts/nni_manager/common/utils.ts +++ b/ts/nni_manager/common/utils.ts @@ -212,8 +212,9 @@ function cleanupUnitTest(): void { Container.restore(TrainingService); Container.restore(DataStore); Container.restore(Database); - setExperimentStartupInfo(true, 'unittest', 8080, 'unittest', undefined, logLevel); Container.restore(ExperimentManager); + const logLevel: string = parseArg(['--log_level', '-ll']); + setExperimentStartupInfo(true, 'unittest', 8080, 'unittest', undefined, logLevel); } let cachedipv4Address: string = '';