Commit cfda0dae authored by demianzhang's avatar demianzhang Committed by SparkSnail
Browse files

NNI on Windows for NNI Local mode (#937)

parent 88ceed71
......@@ -66,6 +66,7 @@ NNI_VERSION_TEMPLATE = 999.0.0-developing
build:
#$(_INFO) Building NNI Manager $(_END)
cd src/nni_manager && $(NNI_YARN) && $(NNI_YARN) build
cp -rf src/nni_manager/config src/nni_manager/dist/
#$(_INFO) Building WebUI $(_END)
cd src/webui && $(NNI_YARN) && $(NNI_YARN) build
......
......@@ -171,3 +171,46 @@ jobs:
fi
condition: eq( variables['upload_package'], 'true')
displayName: 'upload nni package to pypi/testpypi'
- job: 'Build_upload_nni_windows'
dependsOn: version_number_validation
condition: succeeded()
pool:
vmImage: 'vs2017-win2016'
strategy:
matrix:
Python36:
PYTHON_VERSION: '3.6'
steps:
- script: |
python -m pip install --upgrade pip setuptools --user
python -m pip install twine --user
displayName: 'Install twine'
- script: |
cd deployment/pypi
if [ $(build_type) = 'prerelease' ]
then
# NNI build scripts (powershell) uses branch tag as package version number
git tag $(build_version)
echo 'building prerelease package...'
powershell.exe ./install.ps1 -version_ts $True
else
echo 'building release package...'
powershell.exe ./install.ps1
fi
condition: eq( variables['upload_package'], 'true')
displayName: 'build nni bdsit_wheel'
- script: |
cd deployment/pypi
if [ $(build_type) = 'prerelease' ]
then
echo 'uploading prerelease package to testpypi...'
python -m twine upload -u $(testpypi_user) -p $(testpypi_pwd) --repository-url https://test.pypi.org/legacy/ dist/*
else
echo 'uploading release package to pypi...'
python -m twine upload -u $(pypi_user) -p $(pypi_pwd) dist/*
fi
condition: eq( variables['upload_package'], 'true')
displayName: 'upload nni package to pypi/testpypi'
\ No newline at end of file
Python Package Index (PyPI) for NNI
===
# Python Package Index (PyPI) for NNI
## 1.Description
This is the PyPI build and upload tool for NNI project.
## 2.Prepare environment
Before build and upload NNI package, make sure the below OS and tools are available.
```
Ubuntu 16.04 LTS
make
wget
Python >= 3.5
Pip
Node.js
Yarn
```
## 2.How to build
```bash
make
```
## 3.How to upload
### upload for testing
```bash
TWINE_REPOSITORY_URL=https://test.pypi.org/legacy/ make upload
```
You may need to input the account and password of https://test.pypi.org during this process.
### upload for release
```bash
make upload
```
You may need to input the account and password of https://pypi.org during this process.
## **For Linux**
* __Prepare environment__
Before build and upload NNI package, make sure the below OS and tools are available.
```
Ubuntu 16.04 LTS
make
wget
Python >= 3.5
Pip
Node.js
Yarn
```
* __How to build__
```bash
make
```
* __How to upload__
**upload for testing**
```bash
TWINE_REPOSITORY_URL=https://test.pypi.org/legacy/ make upload
```
You may need to input the account and password of https://test.pypi.org during this process.
**upload for release**
```bash
make upload
```
You may need to input the account and password of https://pypi.org during this process.
## **For Windows**
* __Prepare environment__
Before build and upload NNI package, make sure the below OS and tools are available.
```
Windows 10
powershell
Python >= 3.5
Pip
Node.js
Yarn
tar
```
* __How to build__
```bash
powershell ./install.ps1
```
* __How to upload__
**upload for testing**
```bash
powershell ./upload.ps1
```
You may need to input the account and password of https://test.pypi.org during this process.
**upload for release**
```bash
powershell ./upload.ps1 -test $False
```
You may need to input the account and password of https://pypi.org during this process.
\ No newline at end of file
$CWD = $PWD
$OS_SPEC = "windows"
Remove-Item $CWD\build -Recurse -Force
Remove-Item $CWD\dist -Recurse -Force
Remove-Item $CWD\nni -Recurse -Force
Remove-Item $CWD\nni.egg-info -Recurse -Force
Remove-Item $CWD\node-$OS_SPEC-x64 -Recurse -Force
\ No newline at end of file
param([bool]$version_ts=$false)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$CWD = $PWD
$OS_SPEC = "windows"
$WHEEL_SPEC = "win_amd64"
$TIME_STAMP = date -u "+%y%m%d%H%M"
$NNI_VERSION_VALUE = git describe --tags --abbrev=0
# To include time stamp in version value, run:
# make version_ts=true build
if($version_ts){
$NNI_VERSION_VALUE = "$NNI_VERSION_VALUE.$TIME_STAMP"
}
$NNI_VERSION_TEMPLATE = "999.0.0-developing"
python -m pip install --user --upgrade setuptools wheel
$nodeUrl = "https://aka.ms/nni/nodejs-download/win64"
$NNI_NODE_ZIP = "$CWD\node-$OS_SPEC-x64.zip"
$NNI_NODE_FOLDER = "$CWD\node-$OS_SPEC-x64"
(New-Object Net.WebClient).DownloadFile($nodeUrl, $NNI_NODE_ZIP)
if(Test-Path $NNI_NODE_FOLDER){
Remove-Item $NNI_NODE_FOLDER -Recurse -Force
}
New-Item $NNI_NODE_FOLDER -ItemType Directory
cmd /c tar -xf $NNI_NODE_ZIP -C $NNI_NODE_FOLDER --strip-components 1
cd $CWD\..\..\src\nni_manager
yarn
yarn build
cd $CWD\..\..\src\webui
yarn
yarn build
if(Test-Path $CWD\nni){
Remove-Item $CWD\nni -r -fo
}
Copy-Item $CWD\..\..\src\nni_manager\dist $CWD\nni -Recurse
Copy-Item $CWD\..\..\src\webui\build $CWD\nni\static -Recurse
Copy-Item $CWD\..\..\src\nni_manager\package.json $CWD\nni
(Get-Content $CWD\nni\package.json).replace($NNI_VERSION_TEMPLATE, $NNI_VERSION_VALUE) | Set-Content $CWD\nni\package.json
cd $CWD\nni
yarn --prod
cd $CWD
(Get-Content setup.py).replace($NNI_VERSION_TEMPLATE, $NNI_VERSION_VALUE) | Set-Content setup.py
python setup.py bdist_wheel -p $WHEEL_SPEC
\ No newline at end of file
......@@ -27,10 +27,15 @@ if os_type == 'Linux':
os_name = 'POSIX :: Linux'
elif os_type == 'Darwin':
os_name = 'MacOS'
elif os_type == 'Windows':
os_name = 'Microsoft :: Windows'
else:
raise NotImplementedError('current platform {} not supported'.format(os_type))
data_files = [('bin', ['node-{}-x64/bin/node'.format(os_type.lower())])]
if os_type == 'Windows':
data_files = [('.\Scripts', ['node-{}-x64/node.exe'.format(os_type.lower())])]
for (dirpath, dirnames, filenames) in walk('./nni'):
files = [path.normpath(path.join(dirpath, filename)) for filename in filenames]
data_files.append((path.normpath(dirpath), files))
......@@ -69,7 +74,8 @@ setuptools.setup(
'json_tricks',
'numpy',
'scipy',
'coverage'
'coverage',
'colorama'
],
classifiers = [
'Programming Language :: Python :: 3',
......
param([bool]$test=$true)
python -m pip install --user --upgrade twine
if($test){
python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
}
else{
python -m twine upload dist/*
}
\ No newline at end of file
# Installation of NNI
Currently we only support installation on Linux & Mac.
Currently we support installation on Linux, Mac and Windows.
## **Installation**
## **Installation on Linux & Mac**
* __Install NNI through pip__
......@@ -13,7 +13,7 @@ Currently we only support installation on Linux & Mac.
* __Install NNI through source code__
Prerequisite: `python >=3.5, git, wget`
Prerequisite: `python >=3.5`, `git`, `wget`
```bash
git clone -b v0.6 https://github.com/Microsoft/nni.git
cd nni
......@@ -24,6 +24,29 @@ Currently we only support installation on Linux & Mac.
You can also install NNI in a docker image. Please follow the instructions [here](https://github.com/Microsoft/nni/tree/master/deployment/docker/README.md) to build NNI docker image. The NNI docker image can also be retrieved from Docker Hub through the command `docker pull msranni/nni:latest`.
## **Installation on Windows**
* __Install NNI through pip__
Prerequisite: `python >= 3.5`
```bash
python -m pip install --upgrade nni
```
* __Install NNI through source code__
Prerequisite: `python >=3.5`, `git`, `powershell`
When you use powershell to run script for the first time, you need run powershell as Administrator with this command:
```bash
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
```
Then you can install nni as administrator or current user as follows:
```bash
git clone https://github.com/Microsoft/nni.git
cd nni
powershell ./install.ps1
```
## **System requirements**
Below are the minimum system requirements for NNI on Linux. Due to potential programming changes, the minimum system requirements for NNI may change over time.
......@@ -50,6 +73,18 @@ Below are the minimum system requirements for NNI on macOS. Due to potential pro
|**Internet**|Boardband internet connection|
|**Resolution**|1024 x 768 minimum display resolution|
Below are the minimum system requirements for NNI on Windows. Due to potential programming changes, the minimum system requirements for NNI may change over time.
||Minimum Requirements|Recommended Specifications|
|---|---|---|
|**Operating System**|Windows 10|Windows 10|
|**CPU**|Intel® Core™ i3 or AMD Phenom™ X3 8650|Intel® Core™ i5 or AMD Phenom™ II X3 or better|
|**GPU**|NVIDIA® GeForce® GTX 460|NVIDIA® GeForce® GTX 660 or better|
|**Memory**|4 GB RAM|6 GB RAM|
|**Storage**|30 GB available hare drive space|
|**Internet**|Boardband internet connection|
|**Resolution**|1024 x 768 minimum display resolution|
## Further reading
* [Overview](Overview.md)
......
......@@ -43,7 +43,7 @@ def init_params(net):
term_width = 0
try:
_, term_width = os.popen('stty size', 'r').read().split()
term_width = os.get_terminal_size().columns
except Exception as exception:
term_width = 200
term_width = int(term_width)
......
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$install_node = $true
$install_yarn = $true
# nodejs
$nodeUrl = "https://aka.ms/nni/nodejs-download/win64"
$yarnUrl = "https://yarnpkg.com/latest.tar.gz"
$unzipNodeDir = "node-v*"
$unzipYarnDir = "yarn-v*"
$NNI_DEPENDENCY_FOLDER = "C:\tmp\$env:USERNAME"
$WHICH_PYTHON = where.exe python
if($WHICH_PYTHON -eq $null){
throw "Can not find python"
}
else{
$pyVersion = & python -V 2>&1
$pyVersion = ([string]$pyVersion).substring(7,3)
if([double]$pyVersion -lt 3.5){
throw "python version should >= 3.5"
}
}
$WHICH_PIP = where.exe pip
if($WHICH_PIP -eq $null){
throw "Can not find pip"
}
$env:PYTHONIOENCODING = "UTF-8"
if($env:VIRTUAL_ENV){
$NNI_PYTHON3 = $env:VIRTUAL_ENV + "\Scripts"
$NNI_PKG_FOLDER = $env:VIRTUAL_ENV + "\nni"
$NNI_PYTHON_SCRIPTS = $NNI_PYTHON3
}
else{
$NNI_PYTHON3 = $(python -c 'import site; from pathlib import Path; print(Path(site.getsitepackages()[0]))')
$NNI_PKG_FOLDER = $NNI_PYTHON3 + "\nni"
$NNI_PYTHON_SCRIPTS = $NNI_PYTHON3 + "\Scripts"
}
$PIP_INSTALL = """$NNI_PYTHON3\python"" -m pip install ."
if(!(Test-Path $NNI_DEPENDENCY_FOLDER)){
New-Item $NNI_DEPENDENCY_FOLDER -ItemType Directory
}
$NNI_NODE_ZIP = $NNI_DEPENDENCY_FOLDER+"\nni-node.zip"
$NNI_NODE_FOLDER = $NNI_DEPENDENCY_FOLDER+"\nni-node"
$NNI_YARN_TARBALL = $NNI_DEPENDENCY_FOLDER+"\nni-yarn.tar.gz"
$NNI_YARN_FOLDER = $NNI_DEPENDENCY_FOLDER+"\nni-yarn"
$NNI_YARN = $NNI_YARN_FOLDER +"\bin\yarn"
## Version number
$NNI_VERSION_VALUE = $(git describe --tags)
$NNI_VERSION_TEMPLATE = "999.0.0-developing"
if(!(Test-Path $NNI_NODE_ZIP)){
Write-Host "Downloading Node..."
(New-Object Net.WebClient).DownloadFile($nodeUrl, $NNI_NODE_ZIP)
}
if(!(Test-Path $NNI_YARN_TARBALL)){
Write-Host "Downloading Yarn..."
(New-Object Net.WebClient).DownloadFile($yarnUrl, $NNI_YARN_TARBALL)
}
$NNI_YARN_TARBALL = $NNI_YARN_TARBALL -split '\\' -join '\\'
$NNI_DEPENDENCY_FOLDER = $NNI_DEPENDENCY_FOLDER -split '\\' -join '\\'
$SCRIPT_PATH = $NNI_DEPENDENCY_FOLDER + '\extract.py'
$SCRIPT = "import tarfile",
("tar = tarfile.open(""{0}"")" -f $NNI_YARN_TARBALL),
("tar.extractall(""{0}"")" -f $NNI_DEPENDENCY_FOLDER),
"tar.close()"
[System.IO.File]::WriteAllLines($SCRIPT_PATH, $SCRIPT)
Add-Type -AssemblyName System.IO.Compression.FileSystem
function Unzip{
param([string]$zipfile, [string]$outpath)
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
}
if ($install_node) {
### nodejs install
if(!(Test-Path $NNI_NODE_FOLDER)){
Unzip $NNI_NODE_ZIP $NNI_DEPENDENCY_FOLDER
$unzipNodeDir = Get-ChildItem "$NNI_DEPENDENCY_FOLDER\$unzipNodeDir"
Rename-Item $unzipNodeDir "nni-node"
}
Copy-Item "$NNI_NODE_FOLDER\node.exe" $NNI_PYTHON_SCRIPTS -Recurse -Force
### yarn install
if(!(Test-Path $NNI_YARN_FOLDER)){
cmd /C """$NNI_PYTHON3\python""" $SCRIPT_PATH
$unzipYarnDir = Get-ChildItem "$NNI_DEPENDENCY_FOLDER\$unzipYarnDir"
Rename-Item $unzipYarnDir "nni-yarn"
}
}
## install-python-modules:
### Installing Python SDK
(Get-Content setup.py).replace($NNI_VERSION_TEMPLATE, $NNI_VERSION_VALUE) | Set-Content setup.py
cmd /c $PIP_INSTALL
# Building NNI Manager
$env:PATH=$NNI_PYTHON_SCRIPTS+';'+$env:PATH
cd src\nni_manager
cmd /c $NNI_YARN
cmd /c $NNI_YARN build
Copy-Item config -Destination .\dist\ -Recurse -Force
# Building WebUI
cd ..\webui
cmd /c $NNI_YARN
cmd /c $NNI_YARN build
cd ..\..
## install-node-modules
if(!(Test-Path $NNI_PKG_FOLDER)){
New-Item $NNI_PKG_FOLDER -ItemType Directory
}
Remove-Item $NNI_PKG_FOLDER -Recurse -Force
Copy-Item "src\nni_manager\dist" $NNI_PKG_FOLDER -Recurse
Copy-Item "src\nni_manager\package.json" $NNI_PKG_FOLDER
$PKG_JSON = $NNI_PKG_FOLDER + "\package.json"
(Get-Content $PKG_JSON).replace($NNI_VERSION_TEMPLATE, $NNI_VERSION_VALUE) | Set-Content $PKG_JSON
cmd /c $NNI_YARN --prod --cwd $NNI_PKG_FOLDER
$NNI_PKG_FOLDER_STATIC = $NNI_PKG_FOLDER + "\static"
Copy-Item "src\webui\build" $NNI_PKG_FOLDER_STATIC -Recurse
......@@ -55,7 +55,8 @@ setup(
'requests',
'scipy',
'schema',
'PythonWebHDFS'
'PythonWebHDFS',
'colorama'
],
entry_points = {
......
......@@ -22,6 +22,8 @@
import * as assert from 'assert';
import { randomBytes } from 'crypto';
import * as cpp from 'child-process-promise';
import * as cp from 'child_process';
import { ChildProcess, spawn, StdioOptions } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
......@@ -32,6 +34,7 @@ import * as util from 'util';
import { Database, DataStore } from './datastore';
import { ExperimentStartupInfo, getExperimentId, getExperimentStartupInfo, setExperimentStartupInfo } from './experimentStartupInfo';
import { Manager } from './manager';
import { TrialConfig } from '../training_service/common/trialConfig';
import { HyperParameters, TrainingService, TrialJobStatus } from './trainingService';
import { getLogger } from './log';
......@@ -65,7 +68,7 @@ function mkDirP(dirPath: string): Promise<void> {
} else {
const parent: string = path.dirname(dirPath);
mkDirP(parent).then(() => {
fs.mkdir(dirPath, (err: Error) => {
fs.mkdir(dirPath, (err: Error | null) => {
if (err) {
deferred.reject(err);
} else {
......@@ -146,6 +149,23 @@ function parseArg(names: string[]): string {
return '';
}
function encodeCmdLineArgs(args:any):any{
if(process.platform === 'win32'){
return JSON.stringify(args);
}
else{
return JSON.stringify(JSON.stringify(args));
}
}
function getCmdPy():string{
let cmd = 'python3';
if(process.platform === 'win32'){
cmd = 'python';
}
return cmd;
}
/**
* Generate command line to start automl algorithm(s),
* either start advisor or start a process which runs tuner and assessor
......@@ -179,8 +199,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
if (!tuner && !advisor) {
throw new Error('Error: specify neither tuner nor advisor is not allowed');
}
let command: string = `python3 -m nni`;
let command: string = `${getCmdPy()} -m nni`;
if (multiPhase) {
command += ' --multi_phase';
}
......@@ -192,7 +211,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
if (advisor) {
command += ` --advisor_class_name ${advisor.className}`;
if (advisor.classArgs !== undefined) {
command += ` --advisor_args ${JSON.stringify(JSON.stringify(advisor.classArgs))}`;
command += ` --advisor_args ${encodeCmdLineArgs(advisor.classArgs)}`;
}
if (advisor.codeDir !== undefined && advisor.codeDir.length > 1) {
command += ` --advisor_directory ${advisor.codeDir}`;
......@@ -203,7 +222,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
} else {
command += ` --tuner_class_name ${tuner.className}`;
if (tuner.classArgs !== undefined) {
command += ` --tuner_args ${JSON.stringify(JSON.stringify(tuner.classArgs))}`;
command += ` --tuner_args ${encodeCmdLineArgs(tuner.classArgs)}`;
}
if (tuner.codeDir !== undefined && tuner.codeDir.length > 1) {
command += ` --tuner_directory ${tuner.codeDir}`;
......@@ -215,7 +234,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
if (assessor !== undefined && assessor.className !== undefined) {
command += ` --assessor_class_name ${assessor.className}`;
if (assessor.classArgs !== undefined) {
command += ` --assessor_args ${JSON.stringify(JSON.stringify(assessor.classArgs))}`;
command += ` --assessor_args ${encodeCmdLineArgs(assessor.classArgs)}`;
}
if (assessor.codeDir !== undefined && assessor.codeDir.length > 1) {
command += ` --assessor_directory ${assessor.codeDir}`;
......@@ -363,6 +382,83 @@ async function getVersion(): Promise<string> {
return deferred.promise;
}
/**
* run command as ChildProcess
*/
function getTunerProc(command: string, stdio: StdioOptions, newCwd: string, newEnv: any): ChildProcess{
let cmd: string = command;
let arg: string[] = [];
let newShell: boolean = true;
if(process.platform === "win32"){
cmd = command.split(" ", 1)[0];
arg = command.substr(cmd.length+1).split(" ");
newShell = false;
}
const tunerProc: ChildProcess = spawn(cmd, arg, {
stdio,
cwd: newCwd,
env: newEnv,
shell: newShell
});
return tunerProc;
}
/**
* judge whether the process is alive
*/
async function isAlive(pid:any): Promise<boolean>{
let deferred : Deferred<boolean> = new Deferred<boolean>();
let alive: boolean = false;
if(process.platform ==='win32'){
try {
const str = cp.execSync(`powershell.exe Get-Process -Id ${pid} -ErrorAction SilentlyContinue`).toString();
if (str) {
alive = true;
}
}
catch (error) {
}
}
else{
try {
await cpp.exec(`kill -0 ${pid}`);
alive = true;
} catch (error) {
//ignore
}
}
deferred.resolve(alive);
return deferred.promise;
}
/**
* kill process
*/
async function killPid(pid:any): Promise<void>{
let deferred : Deferred<void> = new Deferred<void>();
try {
if (process.platform === "win32") {
await cpp.exec(`cmd /c taskkill /PID ${pid} /F`);
}
else{
await cpp.exec(`kill -9 ${pid}`);
}
} catch (error) {
// pid does not exist, do nothing here
}
deferred.resolve();
return deferred.promise;
}
function getNewLine(): string{
if (process.platform === "win32") {
return "\r\n";
}
else{
return "\n";
}
}
export {countFilesRecursively, getRemoteTmpDir, generateParamFileName, getMsgDispatcherCommand, getCheckpointDir,
getLogDir, getExperimentRootDir, getJobCancelStatus, getDefaultDatabaseDir, getIPV4Address,
mkDirP, delay, prepareUnitTest, parseArg, cleanupUnitTest, uniqueString, randomSelect, getLogLevel, getVersion };
mkDirP, delay, prepareUnitTest, parseArg, cleanupUnitTest, uniqueString, randomSelect, getLogLevel, getVersion, getCmdPy, getTunerProc, isAlive, killPid, getNewLine };
......@@ -35,7 +35,7 @@ import {
import {
TrainingService, TrialJobApplicationForm, TrialJobDetail, TrialJobMetric, TrialJobStatus
} from '../common/trainingService';
import { delay, getCheckpointDir, getExperimentRootDir, getLogDir, getMsgDispatcherCommand, mkDirP, getLogLevel } from '../common/utils';
import { delay, getCheckpointDir, getExperimentRootDir, getLogDir, getMsgDispatcherCommand, mkDirP, getTunerProc, getLogLevel, isAlive, killPid } from '../common/utils';
import {
ADD_CUSTOMIZED_TRIAL_JOB, INITIALIZE, INITIALIZED, KILL_TRIAL_JOB, NEW_TRIAL_JOB, NO_MORE_TRIAL_JOBS, PING,
REPORT_METRIC_DATA, REQUEST_TRIAL_JOBS, SEND_TRIAL_JOB_PARAMETER, TERMINATE, TRIAL_END, UPDATE_SEARCH_SPACE, IMPORT_DATA
......@@ -301,12 +301,7 @@ class NNIManager implements Manager {
NNI_INCLUDE_INTERMEDIATE_RESULTS: includeIntermediateResultsEnv
};
let newEnv = Object.assign({}, process.env, nniEnv);
const tunerProc: ChildProcess = spawn(command, [], {
stdio,
cwd: newCwd,
env: newEnv,
shell: true
});
const tunerProc: ChildProcess = getTunerProc(command,stdio,newCwd,newEnv);
this.dispatcherPid = tunerProc.pid;
this.dispatcher = createDispatcherInterface(tunerProc);
......@@ -352,16 +347,10 @@ class NNIManager implements Manager {
// gracefully terminate tuner and assessor here, wait at most 30 seconds.
for (let i: number = 0; i < 30; i++) {
if (!tunerAlive) { break; }
try {
await cpp.exec(`kill -0 ${this.dispatcherPid}`);
} catch (error) { tunerAlive = false; }
tunerAlive = await isAlive(this.dispatcherPid);
await delay(1000);
}
try {
await cpp.exec(`kill -9 ${this.dispatcherPid}`);
} catch (error) {
// this.tunerPid does not exist, do nothing here
}
await killPid(this.dispatcherPid);
const trialJobList: TrialJobDetail[] = await this.trainingService.listTrialJobs();
// TO DO: to promise all
for (const trialJob of trialJobList) {
......
......@@ -42,6 +42,7 @@ describe('Unit test for dataStore', () => {
});
after(() => {
ds.close();
cleanupUnitTest();
});
......
......@@ -18,11 +18,10 @@
*/
'use strict';
import * as assert from 'assert';
import { ChildProcess, spawn, StdioOptions } from 'child_process';
import { Deferred } from 'ts-deferred';
import { cleanupUnitTest, prepareUnitTest } from '../../common/utils';
import { cleanupUnitTest, prepareUnitTest, getTunerProc, getCmdPy } from '../../common/utils';
import * as CommandType from '../commands';
import { createDispatcherInterface, IpcInterface } from '../ipcInterface';
import { NNIError } from '../../common/errors';
......@@ -39,15 +38,21 @@ function runProcess(): Promise<Error | null> {
// create fake assessor process
const stdio: StdioOptions = ['ignore', 'pipe', process.stderr, 'pipe', 'pipe'];
const proc: ChildProcess = spawn('python3 assessor.py', [], { stdio, cwd: 'core/test', shell: true });
const command: string = getCmdPy() + ' assessor.py';
const proc: ChildProcess = getTunerProc(command, stdio, 'core/test', process.env);
// record its sent/received commands on exit
proc.on('error', (error: Error): void => { deferred.resolve(error); });
proc.on('exit', (code: number): void => {
if (code !== 0) {
deferred.resolve(new Error(`return code: ${code}`));
} else {
sentCommands = proc.stdout.read().toString().split('\n');
let str = proc.stdout.read().toString();
if(str.search("\r\n")!=-1){
sentCommands = str.split("\r\n");
}
else{
sentCommands = str.split('\n');
}
deferred.resolve(null);
}
});
......
......@@ -22,7 +22,7 @@
import * as assert from 'assert';
import { ChildProcess, spawn, StdioOptions } from 'child_process';
import { Deferred } from 'ts-deferred';
import { cleanupUnitTest, prepareUnitTest, getMsgDispatcherCommand } from '../../common/utils';
import { cleanupUnitTest, prepareUnitTest, getMsgDispatcherCommand, getTunerProc } from '../../common/utils';
import * as CommandType from '../commands';
import { createDispatcherInterface, IpcInterface } from '../ipcInterface';
......@@ -50,9 +50,7 @@ function startProcess(): void {
// advisor
undefined
);
const proc: ChildProcess = spawn(dispatcherCmd, [], { stdio, cwd: 'core/test', shell: true });
const proc: ChildProcess = getTunerProc(dispatcherCmd, stdio, 'core/test', process.env);
proc.on('error', (error: Error): void => {
procExit = true;
procError = true;
......
......@@ -33,6 +33,7 @@ import { NNIManager } from '../nnimanager';
import { SqlDB } from '../sqlDatabase';
import { MockedTrainingService } from './mockedTrainingService';
import { MockedDataStore } from './mockedDatastore';
import * as path from 'path';
async function initContainer(): Promise<void> {
prepareUnitTest();
......@@ -183,7 +184,7 @@ describe('Unit test for nnimanager', function () {
it('test getExperimentProfile', () => {
return nniManager.getExperimentProfile().then((experimentProfile) => {
expect(experimentProfile.id).to.be.equal('unittest');
expect(experimentProfile.logDir).to.be.equal(os.homedir()+'/nni/experiments/unittest');
expect(experimentProfile.logDir).to.be.equal(path.join(os.homedir(),'nni','experiments','unittest'));
}).catch((error) => {
assert.fail(error);
......
......@@ -3,7 +3,6 @@
"version": "999.0.0-developing",
"main": "index.js",
"scripts": {
"postbuild": "cp -rf config ./dist/",
"build": "tsc",
"test": "nyc mocha -r ts-node/register -t 15000 --recursive **/*.test.ts --exclude node_modules/**/**/*.test.ts --colors",
"start": "node dist/main.js",
......
......@@ -59,10 +59,17 @@ export class GPUSummary {
}
}
export const GPU_INFO_COLLECTOR_FORMAT: string =
export const GPU_INFO_COLLECTOR_FORMAT_LINUX: string =
`
#!/bin/bash
export METRIC_OUTPUT_DIR={0}
echo $$ >{1}
python3 -m nni_gpu_tool.gpu_metrics_collector
`
export const GPU_INFO_COLLECTOR_FORMAT_WINDOWS: string =
`
$env:METRIC_OUTPUT_DIR="{0}"
$app = Start-Process "python" -ArgumentList "-m nni_gpu_tool.gpu_metrics_collector" -passthru -NoNewWindow
Write $app.ID | Out-File {1} -NoNewline -encoding utf8
`
\ No newline at end of file
......@@ -22,6 +22,12 @@ import { getLogger } from "common/log";
'use strict';
import { countFilesRecursively } from '../../common/utils'
import * as cpp from 'child-process-promise';
import * as cp from 'child_process';
import { GPU_INFO_COLLECTOR_FORMAT_LINUX, GPU_INFO_COLLECTOR_FORMAT_WINDOWS } from './gpuData'
import * as path from 'path';
import { String } from 'typescript-string-operations';
import { file } from "../../node_modules/@types/tmp";
/**
* Validate codeDir, calculate file count recursively under codeDir, and throw error if any rule is broken
......@@ -45,4 +51,131 @@ export async function validateCodeDir(codeDir: string) : Promise<number> {
}
return fileCount;
}
\ No newline at end of file
}
/**
* crete a new directory
* @param directory
*/
export async function execMkdir(directory: string): Promise<void> {
if (process.platform === 'win32') {
await cpp.exec(`powershell.exe New-Item -Path ${directory} -ItemType "directory" -Force`);
} else {
await cpp.exec(`mkdir -p ${directory}`);
}
return Promise.resolve();
}
/**
* crete a new file
* @param filename
*/
export async function execNewFile(filename: string): Promise<void> {
if (process.platform === 'win32') {
await cpp.exec(`powershell.exe New-Item -Path ${filename} -ItemType "file" -Force`);
} else {
await cpp.exec(`touch ${filename}`);
}
return Promise.resolve();
}
/**
* run script
* @param filePath
*/
export function execScript(filePath: string): cp.ChildProcess {
if (process.platform === 'win32') {
return cp.exec(`powershell.exe -file ${filePath}`);
} else {
return cp.exec(`bash ${filePath}`);
}
}
/**
* output the last line of a file
* @param filePath
*/
export async function execTail(filePath: string): Promise<cpp.childProcessPromise.Result> {
let cmdresult: cpp.childProcessPromise.Result;
if (process.platform === 'win32') {
cmdresult = await cpp.exec(`powershell.exe Get-Content ${filePath} -Tail 1`);
} else {
cmdresult = await cpp.exec(`tail -n 1 ${filePath}`);
}
return Promise.resolve(cmdresult);
}
/**
* delete a directory
* @param directory
*/
export async function execRemove(directory: string): Promise<void>{
if (process.platform === 'win32') {
await cpp.exec(`powershell.exe Remove-Item ${directory}`);
} else {
await cpp.exec(`rm -rf ${directory}`);
}
return Promise.resolve();
}
/**
* kill a process
* @param directory
*/
export async function execKill(pid: string): Promise<void>{
if (process.platform === 'win32') {
await cpp.exec(`cmd /c taskkill /PID ${pid} /T /F`);
} else {
await cpp.exec(`pkill -P ${pid}`);
}
return Promise.resolve();
}
/**
* set environment variable
* @param variable
* @returns command string
*/
export function setEnvironmentVariable(variable: { key: string; value: string }): string{
if (process.platform === 'win32') {
return `$env:${variable.key}="${variable.value}"`;
}
else{
return `export ${variable.key}=${variable.value}`;
}
}
/**
* generate script file name
* @param fileNamePrefix
*/
export function getScriptName(fileNamePrefix: string): string {
if (process.platform === 'win32') {
return fileNamePrefix + '.ps1';
} else {
return fileNamePrefix + '.sh';
}
}
/**
* generate script file
* @param gpuMetricCollectorScriptFolder
*/
export function getgpuMetricsCollectorScriptContent(gpuMetricCollectorScriptFolder: string): string {
if(process.platform === 'win32') {
return String.Format(
GPU_INFO_COLLECTOR_FORMAT_WINDOWS,
gpuMetricCollectorScriptFolder,
path.join(gpuMetricCollectorScriptFolder, 'pid'),
);
} else {
return String.Format(
GPU_INFO_COLLECTOR_FORMAT_LINUX,
gpuMetricCollectorScriptFolder,
path.join(gpuMetricCollectorScriptFolder, 'pid'),
);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment