"src/git@developer.sourcefind.cn:OpenDAS/tilelang.git" did not exist on "a03df604e43e6c56498852a0fb5e89bd8d25a7b6"
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 ...@@ -66,6 +66,7 @@ NNI_VERSION_TEMPLATE = 999.0.0-developing
build: build:
#$(_INFO) Building NNI Manager $(_END) #$(_INFO) Building NNI Manager $(_END)
cd src/nni_manager && $(NNI_YARN) && $(NNI_YARN) build cd src/nni_manager && $(NNI_YARN) && $(NNI_YARN) build
cp -rf src/nni_manager/config src/nni_manager/dist/
#$(_INFO) Building WebUI $(_END) #$(_INFO) Building WebUI $(_END)
cd src/webui && $(NNI_YARN) && $(NNI_YARN) build cd src/webui && $(NNI_YARN) && $(NNI_YARN) build
......
...@@ -171,3 +171,46 @@ jobs: ...@@ -171,3 +171,46 @@ jobs:
fi fi
condition: eq( variables['upload_package'], 'true') condition: eq( variables['upload_package'], 'true')
displayName: 'upload nni package to pypi/testpypi' 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. This is the PyPI build and upload tool for NNI project.
## 2.Prepare environment ## **For Linux**
Before build and upload NNI package, make sure the below OS and tools are available.
``` * __Prepare environment__
Ubuntu 16.04 LTS
make Before build and upload NNI package, make sure the below OS and tools are available.
wget ```
Python >= 3.5 Ubuntu 16.04 LTS
Pip make
Node.js wget
Yarn Python >= 3.5
``` Pip
Node.js
## 2.How to build Yarn
```bash ```
make
``` * __How to build__
## 3.How to upload ```bash
make
### upload for testing ```
```bash
TWINE_REPOSITORY_URL=https://test.pypi.org/legacy/ make upload * __How to upload__
```
You may need to input the account and password of https://test.pypi.org during this process. **upload for testing**
```bash
### upload for release TWINE_REPOSITORY_URL=https://test.pypi.org/legacy/ make upload
```bash ```
make upload You may need to input the account and password of https://test.pypi.org during this process.
```
You may need to input the account and password of https://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': ...@@ -27,10 +27,15 @@ if os_type == 'Linux':
os_name = 'POSIX :: Linux' os_name = 'POSIX :: Linux'
elif os_type == 'Darwin': elif os_type == 'Darwin':
os_name = 'MacOS' os_name = 'MacOS'
elif os_type == 'Windows':
os_name = 'Microsoft :: Windows'
else: else:
raise NotImplementedError('current platform {} not supported'.format(os_type)) raise NotImplementedError('current platform {} not supported'.format(os_type))
data_files = [('bin', ['node-{}-x64/bin/node'.format(os_type.lower())])] 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'): for (dirpath, dirnames, filenames) in walk('./nni'):
files = [path.normpath(path.join(dirpath, filename)) for filename in filenames] files = [path.normpath(path.join(dirpath, filename)) for filename in filenames]
data_files.append((path.normpath(dirpath), files)) data_files.append((path.normpath(dirpath), files))
...@@ -69,7 +74,8 @@ setuptools.setup( ...@@ -69,7 +74,8 @@ setuptools.setup(
'json_tricks', 'json_tricks',
'numpy', 'numpy',
'scipy', 'scipy',
'coverage' 'coverage',
'colorama'
], ],
classifiers = [ classifiers = [
'Programming Language :: Python :: 3', '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 # 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__ * __Install NNI through pip__
...@@ -13,7 +13,7 @@ Currently we only support installation on Linux & Mac. ...@@ -13,7 +13,7 @@ Currently we only support installation on Linux & Mac.
* __Install NNI through source code__ * __Install NNI through source code__
Prerequisite: `python >=3.5, git, wget` Prerequisite: `python >=3.5`, `git`, `wget`
```bash ```bash
git clone -b v0.6 https://github.com/Microsoft/nni.git git clone -b v0.6 https://github.com/Microsoft/nni.git
cd nni cd nni
...@@ -24,6 +24,29 @@ Currently we only support installation on Linux & Mac. ...@@ -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`. 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** ## **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. 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 ...@@ -50,6 +73,18 @@ Below are the minimum system requirements for NNI on macOS. Due to potential pro
|**Internet**|Boardband internet connection| |**Internet**|Boardband internet connection|
|**Resolution**|1024 x 768 minimum display resolution| |**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 ## Further reading
* [Overview](Overview.md) * [Overview](Overview.md)
......
...@@ -43,7 +43,7 @@ def init_params(net): ...@@ -43,7 +43,7 @@ def init_params(net):
term_width = 0 term_width = 0
try: try:
_, term_width = os.popen('stty size', 'r').read().split() term_width = os.get_terminal_size().columns
except Exception as exception: except Exception as exception:
term_width = 200 term_width = 200
term_width = int(term_width) 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( ...@@ -55,7 +55,8 @@ setup(
'requests', 'requests',
'scipy', 'scipy',
'schema', 'schema',
'PythonWebHDFS' 'PythonWebHDFS',
'colorama'
], ],
entry_points = { entry_points = {
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import * as cpp from 'child-process-promise'; 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 fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
...@@ -32,6 +34,7 @@ import * as util from 'util'; ...@@ -32,6 +34,7 @@ import * as util from 'util';
import { Database, DataStore } from './datastore'; import { Database, DataStore } from './datastore';
import { ExperimentStartupInfo, getExperimentId, getExperimentStartupInfo, setExperimentStartupInfo } from './experimentStartupInfo'; import { ExperimentStartupInfo, getExperimentId, getExperimentStartupInfo, setExperimentStartupInfo } from './experimentStartupInfo';
import { Manager } from './manager'; import { Manager } from './manager';
import { TrialConfig } from '../training_service/common/trialConfig';
import { HyperParameters, TrainingService, TrialJobStatus } from './trainingService'; import { HyperParameters, TrainingService, TrialJobStatus } from './trainingService';
import { getLogger } from './log'; import { getLogger } from './log';
...@@ -65,7 +68,7 @@ function mkDirP(dirPath: string): Promise<void> { ...@@ -65,7 +68,7 @@ function mkDirP(dirPath: string): Promise<void> {
} else { } else {
const parent: string = path.dirname(dirPath); const parent: string = path.dirname(dirPath);
mkDirP(parent).then(() => { mkDirP(parent).then(() => {
fs.mkdir(dirPath, (err: Error) => { fs.mkdir(dirPath, (err: Error | null) => {
if (err) { if (err) {
deferred.reject(err); deferred.reject(err);
} else { } else {
...@@ -146,6 +149,23 @@ function parseArg(names: string[]): string { ...@@ -146,6 +149,23 @@ function parseArg(names: string[]): string {
return ''; 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), * Generate command line to start automl algorithm(s),
* either start advisor or start a process which runs tuner and assessor * 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 ...@@ -179,8 +199,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
if (!tuner && !advisor) { if (!tuner && !advisor) {
throw new Error('Error: specify neither tuner nor advisor is not allowed'); throw new Error('Error: specify neither tuner nor advisor is not allowed');
} }
let command: string = `${getCmdPy()} -m nni`;
let command: string = `python3 -m nni`;
if (multiPhase) { if (multiPhase) {
command += ' --multi_phase'; command += ' --multi_phase';
} }
...@@ -192,7 +211,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP ...@@ -192,7 +211,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
if (advisor) { if (advisor) {
command += ` --advisor_class_name ${advisor.className}`; command += ` --advisor_class_name ${advisor.className}`;
if (advisor.classArgs !== undefined) { 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) { if (advisor.codeDir !== undefined && advisor.codeDir.length > 1) {
command += ` --advisor_directory ${advisor.codeDir}`; command += ` --advisor_directory ${advisor.codeDir}`;
...@@ -203,7 +222,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP ...@@ -203,7 +222,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
} else { } else {
command += ` --tuner_class_name ${tuner.className}`; command += ` --tuner_class_name ${tuner.className}`;
if (tuner.classArgs !== undefined) { 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) { if (tuner.codeDir !== undefined && tuner.codeDir.length > 1) {
command += ` --tuner_directory ${tuner.codeDir}`; command += ` --tuner_directory ${tuner.codeDir}`;
...@@ -215,7 +234,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP ...@@ -215,7 +234,7 @@ function getMsgDispatcherCommand(tuner: any, assessor: any, advisor: any, multiP
if (assessor !== undefined && assessor.className !== undefined) { if (assessor !== undefined && assessor.className !== undefined) {
command += ` --assessor_class_name ${assessor.className}`; command += ` --assessor_class_name ${assessor.className}`;
if (assessor.classArgs !== undefined) { 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) { if (assessor.codeDir !== undefined && assessor.codeDir.length > 1) {
command += ` --assessor_directory ${assessor.codeDir}`; command += ` --assessor_directory ${assessor.codeDir}`;
...@@ -363,6 +382,83 @@ async function getVersion(): Promise<string> { ...@@ -363,6 +382,83 @@ async function getVersion(): Promise<string> {
return deferred.promise; 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, export {countFilesRecursively, getRemoteTmpDir, generateParamFileName, getMsgDispatcherCommand, getCheckpointDir,
getLogDir, getExperimentRootDir, getJobCancelStatus, getDefaultDatabaseDir, getIPV4Address, 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 { ...@@ -35,7 +35,7 @@ import {
import { import {
TrainingService, TrialJobApplicationForm, TrialJobDetail, TrialJobMetric, TrialJobStatus TrainingService, TrialJobApplicationForm, TrialJobDetail, TrialJobMetric, TrialJobStatus
} from '../common/trainingService'; } 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 { import {
ADD_CUSTOMIZED_TRIAL_JOB, INITIALIZE, INITIALIZED, KILL_TRIAL_JOB, NEW_TRIAL_JOB, NO_MORE_TRIAL_JOBS, PING, 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 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 { ...@@ -301,12 +301,7 @@ class NNIManager implements Manager {
NNI_INCLUDE_INTERMEDIATE_RESULTS: includeIntermediateResultsEnv NNI_INCLUDE_INTERMEDIATE_RESULTS: includeIntermediateResultsEnv
}; };
let newEnv = Object.assign({}, process.env, nniEnv); let newEnv = Object.assign({}, process.env, nniEnv);
const tunerProc: ChildProcess = spawn(command, [], { const tunerProc: ChildProcess = getTunerProc(command,stdio,newCwd,newEnv);
stdio,
cwd: newCwd,
env: newEnv,
shell: true
});
this.dispatcherPid = tunerProc.pid; this.dispatcherPid = tunerProc.pid;
this.dispatcher = createDispatcherInterface(tunerProc); this.dispatcher = createDispatcherInterface(tunerProc);
...@@ -352,16 +347,10 @@ class NNIManager implements Manager { ...@@ -352,16 +347,10 @@ class NNIManager implements Manager {
// gracefully terminate tuner and assessor here, wait at most 30 seconds. // gracefully terminate tuner and assessor here, wait at most 30 seconds.
for (let i: number = 0; i < 30; i++) { for (let i: number = 0; i < 30; i++) {
if (!tunerAlive) { break; } if (!tunerAlive) { break; }
try { tunerAlive = await isAlive(this.dispatcherPid);
await cpp.exec(`kill -0 ${this.dispatcherPid}`);
} catch (error) { tunerAlive = false; }
await delay(1000); await delay(1000);
} }
try { await killPid(this.dispatcherPid);
await cpp.exec(`kill -9 ${this.dispatcherPid}`);
} catch (error) {
// this.tunerPid does not exist, do nothing here
}
const trialJobList: TrialJobDetail[] = await this.trainingService.listTrialJobs(); const trialJobList: TrialJobDetail[] = await this.trainingService.listTrialJobs();
// TO DO: to promise all // TO DO: to promise all
for (const trialJob of trialJobList) { for (const trialJob of trialJobList) {
......
...@@ -42,6 +42,7 @@ describe('Unit test for dataStore', () => { ...@@ -42,6 +42,7 @@ describe('Unit test for dataStore', () => {
}); });
after(() => { after(() => {
ds.close();
cleanupUnitTest(); cleanupUnitTest();
}); });
......
...@@ -18,11 +18,10 @@ ...@@ -18,11 +18,10 @@
*/ */
'use strict'; 'use strict';
import * as assert from 'assert'; import * as assert from 'assert';
import { ChildProcess, spawn, StdioOptions } from 'child_process'; import { ChildProcess, spawn, StdioOptions } from 'child_process';
import { Deferred } from 'ts-deferred'; 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 * as CommandType from '../commands';
import { createDispatcherInterface, IpcInterface } from '../ipcInterface'; import { createDispatcherInterface, IpcInterface } from '../ipcInterface';
import { NNIError } from '../../common/errors'; import { NNIError } from '../../common/errors';
...@@ -39,15 +38,21 @@ function runProcess(): Promise<Error | null> { ...@@ -39,15 +38,21 @@ function runProcess(): Promise<Error | null> {
// create fake assessor process // create fake assessor process
const stdio: StdioOptions = ['ignore', 'pipe', process.stderr, 'pipe', 'pipe']; 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 // record its sent/received commands on exit
proc.on('error', (error: Error): void => { deferred.resolve(error); }); proc.on('error', (error: Error): void => { deferred.resolve(error); });
proc.on('exit', (code: number): void => { proc.on('exit', (code: number): void => {
if (code !== 0) { if (code !== 0) {
deferred.resolve(new Error(`return code: ${code}`)); deferred.resolve(new Error(`return code: ${code}`));
} else { } 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); deferred.resolve(null);
} }
}); });
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { ChildProcess, spawn, StdioOptions } from 'child_process'; import { ChildProcess, spawn, StdioOptions } from 'child_process';
import { Deferred } from 'ts-deferred'; 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 * as CommandType from '../commands';
import { createDispatcherInterface, IpcInterface } from '../ipcInterface'; import { createDispatcherInterface, IpcInterface } from '../ipcInterface';
...@@ -50,9 +50,7 @@ function startProcess(): void { ...@@ -50,9 +50,7 @@ function startProcess(): void {
// advisor // advisor
undefined undefined
); );
const proc: ChildProcess = getTunerProc(dispatcherCmd, stdio, 'core/test', process.env);
const proc: ChildProcess = spawn(dispatcherCmd, [], { stdio, cwd: 'core/test', shell: true });
proc.on('error', (error: Error): void => { proc.on('error', (error: Error): void => {
procExit = true; procExit = true;
procError = true; procError = true;
......
...@@ -33,6 +33,7 @@ import { NNIManager } from '../nnimanager'; ...@@ -33,6 +33,7 @@ import { NNIManager } from '../nnimanager';
import { SqlDB } from '../sqlDatabase'; import { SqlDB } from '../sqlDatabase';
import { MockedTrainingService } from './mockedTrainingService'; import { MockedTrainingService } from './mockedTrainingService';
import { MockedDataStore } from './mockedDatastore'; import { MockedDataStore } from './mockedDatastore';
import * as path from 'path';
async function initContainer(): Promise<void> { async function initContainer(): Promise<void> {
prepareUnitTest(); prepareUnitTest();
...@@ -183,7 +184,7 @@ describe('Unit test for nnimanager', function () { ...@@ -183,7 +184,7 @@ describe('Unit test for nnimanager', function () {
it('test getExperimentProfile', () => { it('test getExperimentProfile', () => {
return nniManager.getExperimentProfile().then((experimentProfile) => { return nniManager.getExperimentProfile().then((experimentProfile) => {
expect(experimentProfile.id).to.be.equal('unittest'); 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) => { }).catch((error) => {
assert.fail(error); assert.fail(error);
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
"version": "999.0.0-developing", "version": "999.0.0-developing",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"postbuild": "cp -rf config ./dist/",
"build": "tsc", "build": "tsc",
"test": "nyc mocha -r ts-node/register -t 15000 --recursive **/*.test.ts --exclude node_modules/**/**/*.test.ts --colors", "test": "nyc mocha -r ts-node/register -t 15000 --recursive **/*.test.ts --exclude node_modules/**/**/*.test.ts --colors",
"start": "node dist/main.js", "start": "node dist/main.js",
......
...@@ -59,10 +59,17 @@ export class GPUSummary { ...@@ -59,10 +59,17 @@ export class GPUSummary {
} }
} }
export const GPU_INFO_COLLECTOR_FORMAT: string = export const GPU_INFO_COLLECTOR_FORMAT_LINUX: string =
` `
#!/bin/bash #!/bin/bash
export METRIC_OUTPUT_DIR={0} export METRIC_OUTPUT_DIR={0}
echo $$ >{1} echo $$ >{1}
python3 -m nni_gpu_tool.gpu_metrics_collector 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"; ...@@ -22,6 +22,12 @@ import { getLogger } from "common/log";
'use strict'; 'use strict';
import { countFilesRecursively } from '../../common/utils' 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 * 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> { ...@@ -45,4 +51,131 @@ export async function validateCodeDir(codeDir: string) : Promise<number> {
} }
return fileCount; 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