Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
593a275c
Commit
593a275c
authored
Dec 14, 2020
by
Yuge Zhang
Browse files
Merge branch 'master' of
https://github.com/microsoft/nni
into dev-retiarii
parents
b3cdee85
683c458a
Changes
85
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
785 additions
and
161 deletions
+785
-161
setup.py
setup.py
+26
-2
ts/nni_manager/rest_server/restValidationSchemas.ts
ts/nni_manager/rest_server/restValidationSchemas.ts
+1
-0
ts/nni_manager/training_service/kubernetes/adl/adlApiClient.ts
...i_manager/training_service/kubernetes/adl/adlApiClient.ts
+8
-5
ts/nni_manager/training_service/kubernetes/adl/adlConfig.ts
ts/nni_manager/training_service/kubernetes/adl/adlConfig.ts
+5
-1
ts/nni_manager/training_service/kubernetes/adl/adlJobInfoCollector.ts
...er/training_service/kubernetes/adl/adlJobInfoCollector.ts
+5
-5
ts/nni_manager/training_service/kubernetes/adl/adlTrainingService.ts
...ger/training_service/kubernetes/adl/adlTrainingService.ts
+20
-2
ts/nni_manager/training_service/kubernetes/kubernetesApiClient.ts
...anager/training_service/kubernetes/kubernetesApiClient.ts
+13
-7
ts/nni_manager/training_service/kubernetes/kubernetesTrainingService.ts
.../training_service/kubernetes/kubernetesTrainingService.ts
+0
-7
ts/nni_manager/training_service/local/gpuScheduler.ts
ts/nni_manager/training_service/local/gpuScheduler.ts
+2
-2
ts/nni_manager/training_service/local/localTrainingService.ts
...ni_manager/training_service/local/localTrainingService.ts
+2
-2
ts/webui/src/App.scss
ts/webui/src/App.scss
+2
-0
ts/webui/src/App.tsx
ts/webui/src/App.tsx
+58
-69
ts/webui/src/components/NavCon.tsx
ts/webui/src/components/NavCon.tsx
+45
-56
ts/webui/src/components/NavConst.ts
ts/webui/src/components/NavConst.ts
+15
-0
ts/webui/src/components/Overview.tsx
ts/webui/src/components/Overview.tsx
+2
-2
ts/webui/src/components/buttons/Icon.tsx
ts/webui/src/components/buttons/Icon.tsx
+7
-1
ts/webui/src/components/managementExp/ExperimentManager.tsx
ts/webui/src/components/managementExp/ExperimentManager.tsx
+434
-0
ts/webui/src/components/managementExp/FilterBtns.tsx
ts/webui/src/components/managementExp/FilterBtns.tsx
+81
-0
ts/webui/src/components/managementExp/Header.tsx
ts/webui/src/components/managementExp/Header.tsx
+26
-0
ts/webui/src/components/managementExp/TrialIdColumn.tsx
ts/webui/src/components/managementExp/TrialIdColumn.tsx
+33
-0
No files found.
setup.py
View file @
593a275c
...
@@ -65,7 +65,6 @@ dependencies = [
...
@@ -65,7 +65,6 @@ dependencies = [
'ruamel.yaml'
,
'ruamel.yaml'
,
'requests'
,
'requests'
,
'responses'
,
'responses'
,
'scipy'
,
'schema'
,
'schema'
,
'PythonWebHDFS'
,
'PythonWebHDFS'
,
'colorama'
,
'colorama'
,
...
@@ -77,7 +76,9 @@ dependencies = [
...
@@ -77,7 +76,9 @@ dependencies = [
'dataclasses ; python_version < "3.7"'
,
'dataclasses ; python_version < "3.7"'
,
'numpy < 1.19.4 ; sys_platform == "win32"'
,
'numpy < 1.19.4 ; sys_platform == "win32"'
,
'numpy < 1.20 ; sys_platform != "win32" and python_version < "3.7"'
,
'numpy < 1.20 ; sys_platform != "win32" and python_version < "3.7"'
,
'numpy ; sys.platform != "win32" and python_version >= "3.7"'
'numpy ; sys.platform != "win32" and python_version >= "3.7"'
,
'scipy < 1.6 ; python_version < "3.7"'
,
'scipy ; python_version >= "3.7"'
,
]
]
release
=
os
.
environ
.
get
(
'NNI_RELEASE'
)
release
=
os
.
environ
.
get
(
'NNI_RELEASE'
)
...
@@ -110,6 +111,14 @@ def _setup():
...
@@ -110,6 +111,14 @@ def _setup():
python_requires
=
'>=3.6'
,
python_requires
=
'>=3.6'
,
install_requires
=
dependencies
,
install_requires
=
dependencies
,
extras_require
=
{
'SMAC'
:
[
'ConfigSpaceNNI @ git+https://github.com/QuanluZhang/ConfigSpace.git'
,
'smac @ git+https://github.com/QuanluZhang/SMAC3.git'
],
'BOHB'
:
[
'ConfigSpace==0.4.7'
,
'statsmodels==0.10.0'
],
'PPOTuner'
:
[
'enum34'
,
'gym'
]
},
setup_requires
=
[
'requests'
],
setup_requires
=
[
'requests'
],
entry_points
=
{
entry_points
=
{
...
@@ -157,6 +166,19 @@ def _find_node_files():
...
@@ -157,6 +166,19 @@ def _find_node_files():
def
_using_conda_or_virtual_environment
():
def
_using_conda_or_virtual_environment
():
return
sys
.
prefix
!=
sys
.
base_prefix
or
os
.
path
.
isdir
(
os
.
path
.
join
(
sys
.
prefix
,
'conda-meta'
))
return
sys
.
prefix
!=
sys
.
base_prefix
or
os
.
path
.
isdir
(
os
.
path
.
join
(
sys
.
prefix
,
'conda-meta'
))
def
_copy_data_files
():
# after installation, nni needs to find this location in nni.tools.package_utils.get_registered_algo_config_path
# since we can not import nni here, we need to ensure get_registered_algo_config_path use the same
# logic here to retrieve registered_algorithms.yml
if
_using_conda_or_virtual_environment
():
nni_config_dir
=
os
.
path
.
join
(
sys
.
prefix
,
'nni'
)
elif
sys
.
platform
==
'win32'
:
nni_config_dir
=
os
.
path
.
join
(
os
.
getenv
(
'APPDATA'
),
'nni'
)
else
:
nni_config_dir
=
os
.
path
.
expanduser
(
'~/.config/nni'
)
if
not
os
.
path
.
exists
(
nni_config_dir
):
os
.
makedirs
(
nni_config_dir
)
shutil
.
copyfile
(
'./deployment/registered_algorithms.yml'
,
os
.
path
.
join
(
nni_config_dir
,
'registered_algorithms.yml'
))
class
BuildTs
(
Command
):
class
BuildTs
(
Command
):
description
=
'build TypeScript modules'
description
=
'build TypeScript modules'
...
@@ -178,6 +200,7 @@ class Build(build):
...
@@ -178,6 +200,7 @@ class Build(build):
sys
.
exit
(
'Please set environment variable "NNI_RELEASE=<release_version>"'
)
sys
.
exit
(
'Please set environment variable "NNI_RELEASE=<release_version>"'
)
if
os
.
path
.
islink
(
'nni_node/main.js'
):
if
os
.
path
.
islink
(
'nni_node/main.js'
):
sys
.
exit
(
'A development build already exists. Please uninstall NNI and run "python3 setup.py clean --all".'
)
sys
.
exit
(
'A development build already exists. Please uninstall NNI and run "python3 setup.py clean --all".'
)
_copy_data_files
()
super
().
run
()
super
().
run
()
class
Develop
(
develop
):
class
Develop
(
develop
):
...
@@ -203,6 +226,7 @@ class Develop(develop):
...
@@ -203,6 +226,7 @@ class Develop(develop):
def
run
(
self
):
def
run
(
self
):
if
not
self
.
skip_ts
:
if
not
self
.
skip_ts
:
setup_ts
.
build
(
release
=
None
)
setup_ts
.
build
(
release
=
None
)
_copy_data_files
()
super
().
run
()
super
().
run
()
class
Clean
(
clean
):
class
Clean
(
clean
):
...
...
ts/nni_manager/rest_server/restValidationSchemas.ts
View file @
593a275c
...
@@ -101,6 +101,7 @@ export namespace ValidationSchemas {
...
@@ -101,6 +101,7 @@ export namespace ValidationSchemas {
name
:
joi
.
string
().
min
(
1
).
required
()
name
:
joi
.
string
().
min
(
1
).
required
()
}),
}),
// ############## adl ###############
// ############## adl ###############
namespace
:
joi
.
string
(),
adaptive
:
joi
.
boolean
(),
adaptive
:
joi
.
boolean
(),
checkpoint
:
joi
.
object
({
checkpoint
:
joi
.
object
({
storageClass
:
joi
.
string
().
min
(
1
).
required
(),
storageClass
:
joi
.
string
().
min
(
1
).
required
(),
...
...
ts/nni_manager/training_service/kubernetes/adl/adlApiClient.ts
View file @
593a275c
...
@@ -13,14 +13,17 @@ class AdlClientV1 extends KubernetesCRDClient {
...
@@ -13,14 +13,17 @@ class AdlClientV1 extends KubernetesCRDClient {
/**
/**
* constructor, to initialize adl CRD definition
* constructor, to initialize adl CRD definition
*/
*/
public
constructor
()
{
protected
readonly
namespace
:
string
;
public
constructor
(
namespace
:
string
)
{
super
();
super
();
this
.
namespace
=
namespace
;
this
.
crdSchema
=
JSON
.
parse
(
fs
.
readFileSync
(
'
./config/adl/adaptdl-crd-v1.json
'
,
'
utf8
'
));
this
.
crdSchema
=
JSON
.
parse
(
fs
.
readFileSync
(
'
./config/adl/adaptdl-crd-v1.json
'
,
'
utf8
'
));
this
.
client
.
addCustomResourceDefinition
(
this
.
crdSchema
);
this
.
client
.
addCustomResourceDefinition
(
this
.
crdSchema
);
}
}
protected
get
operator
():
any
{
protected
get
operator
():
any
{
return
this
.
client
.
apis
[
'
adaptdl.petuum.com
'
].
v1
.
namespaces
(
'
default
'
).
adaptdljobs
;
return
this
.
client
.
apis
[
'
adaptdl.petuum.com
'
].
v1
.
namespaces
(
this
.
namespace
).
adaptdljobs
;
}
}
public
get
containerName
():
string
{
public
get
containerName
():
string
{
...
@@ -29,7 +32,7 @@ class AdlClientV1 extends KubernetesCRDClient {
...
@@ -29,7 +32,7 @@ class AdlClientV1 extends KubernetesCRDClient {
public
async
getKubernetesPods
(
jobName
:
string
):
Promise
<
any
>
{
public
async
getKubernetesPods
(
jobName
:
string
):
Promise
<
any
>
{
let
result
:
Promise
<
any
>
;
let
result
:
Promise
<
any
>
;
const
response
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
).
pods
const
response
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
).
pods
.
get
({
qs
:
{
labelSelector
:
`adaptdl/job=
${
jobName
}
`
}
});
.
get
({
qs
:
{
labelSelector
:
`adaptdl/job=
${
jobName
}
`
}
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
response
.
body
);
result
=
Promise
.
resolve
(
response
.
body
);
...
@@ -47,8 +50,8 @@ class AdlClientFactory {
...
@@ -47,8 +50,8 @@ class AdlClientFactory {
/**
/**
* Factory method to generate operator client
* Factory method to generate operator client
*/
*/
public
static
createClient
():
KubernetesCRDClient
{
public
static
createClient
(
namespace
:
string
):
KubernetesCRDClient
{
return
new
AdlClientV1
();
return
new
AdlClientV1
(
namespace
);
}
}
}
}
...
...
ts/nni_manager/training_service/kubernetes/adl/adlConfig.ts
View file @
593a275c
...
@@ -58,6 +58,8 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
...
@@ -58,6 +58,8 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
public
readonly
image
:
string
;
public
readonly
image
:
string
;
public
readonly
namespace
?:
string
;
public
readonly
imagePullSecrets
?:
ImagePullSecretConfig
[];
public
readonly
imagePullSecrets
?:
ImagePullSecretConfig
[];
public
readonly
nfs
?:
NFSConfig
;
public
readonly
nfs
?:
NFSConfig
;
...
@@ -72,7 +74,8 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
...
@@ -72,7 +74,8 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
constructor
(
codeDir
:
string
,
constructor
(
codeDir
:
string
,
command
:
string
,
gpuNum
:
number
,
command
:
string
,
gpuNum
:
number
,
image
:
string
,
imagePullSecrets
?:
ImagePullSecretConfig
[],
image
:
string
,
namespace
?:
string
,
imagePullSecrets
?:
ImagePullSecretConfig
[],
nfs
?:
NFSConfig
,
checkpoint
?:
CheckpointConfig
,
nfs
?:
NFSConfig
,
checkpoint
?:
CheckpointConfig
,
cpuNum
?:
number
,
memorySize
?:
string
,
cpuNum
?:
number
,
memorySize
?:
string
,
adaptive
?:
boolean
adaptive
?:
boolean
...
@@ -81,6 +84,7 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
...
@@ -81,6 +84,7 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
this
.
command
=
command
;
this
.
command
=
command
;
this
.
gpuNum
=
gpuNum
;
this
.
gpuNum
=
gpuNum
;
this
.
image
=
image
;
this
.
image
=
image
;
this
.
namespace
=
namespace
;
this
.
imagePullSecrets
=
imagePullSecrets
;
this
.
imagePullSecrets
=
imagePullSecrets
;
this
.
nfs
=
nfs
;
this
.
nfs
=
nfs
;
this
.
checkpoint
=
checkpoint
;
this
.
checkpoint
=
checkpoint
;
...
...
ts/nni_manager/training_service/kubernetes/adl/adlJobInfoCollector.ts
View file @
593a275c
...
@@ -16,21 +16,21 @@ export class AdlJobInfoCollector extends KubernetesJobInfoCollector {
...
@@ -16,21 +16,21 @@ export class AdlJobInfoCollector extends KubernetesJobInfoCollector {
super
(
jobMap
);
super
(
jobMap
);
}
}
protected
async
retrieveSingleTrialJobInfo
(
kubernetesCRD
Client
:
AdlClientV1
|
undefined
,
protected
async
retrieveSingleTrialJobInfo
(
adl
Client
:
AdlClientV1
|
undefined
,
kubernetesTrialJob
:
KubernetesTrialJobDetail
):
Promise
<
void
>
{
kubernetesTrialJob
:
KubernetesTrialJobDetail
):
Promise
<
void
>
{
if
(
!
this
.
statusesNeedToCheck
.
includes
(
kubernetesTrialJob
.
status
))
{
if
(
!
this
.
statusesNeedToCheck
.
includes
(
kubernetesTrialJob
.
status
))
{
return
Promise
.
resolve
();
return
Promise
.
resolve
();
}
}
if
(
kubernetesCRD
Client
===
undefined
)
{
if
(
adl
Client
===
undefined
)
{
return
Promise
.
reject
(
'
kubernetesCRD
Client is undefined
'
);
return
Promise
.
reject
(
'
Adl
Client is undefined
'
);
}
}
let
kubernetesJobInfo
:
any
;
let
kubernetesJobInfo
:
any
;
let
kubernetesPodsInfo
:
any
;
let
kubernetesPodsInfo
:
any
;
try
{
try
{
kubernetesJobInfo
=
await
kubernetesCRD
Client
.
getKubernetesJob
(
kubernetesTrialJob
.
kubernetesJobName
);
kubernetesJobInfo
=
await
adl
Client
.
getKubernetesJob
(
kubernetesTrialJob
.
kubernetesJobName
);
kubernetesPodsInfo
=
await
kubernetesCRD
Client
.
getKubernetesPods
(
kubernetesTrialJob
.
kubernetesJobName
);
kubernetesPodsInfo
=
await
adl
Client
.
getKubernetesPods
(
kubernetesTrialJob
.
kubernetesJobName
);
}
catch
(
error
)
{
}
catch
(
error
)
{
// Notice: it maynot be a 'real' error since cancel trial job can also cause getKubernetesJob failed.
// Notice: it maynot be a 'real' error since cancel trial job can also cause getKubernetesJob failed.
this
.
log
.
error
(
`Get job
${
kubernetesTrialJob
.
kubernetesJobName
}
info failed, error is
${
error
}
`
);
this
.
log
.
error
(
`Get job
${
kubernetesTrialJob
.
kubernetesJobName
}
info failed, error is
${
error
}
`
);
...
...
ts/nni_manager/training_service/kubernetes/adl/adlTrainingService.ts
View file @
593a275c
...
@@ -39,7 +39,6 @@ class AdlTrainingService extends KubernetesTrainingService implements Kubernetes
...
@@ -39,7 +39,6 @@ class AdlTrainingService extends KubernetesTrainingService implements Kubernetes
super
();
super
();
this
.
adlJobInfoCollector
=
new
AdlJobInfoCollector
(
this
.
trialJobsMap
);
this
.
adlJobInfoCollector
=
new
AdlJobInfoCollector
(
this
.
trialJobsMap
);
this
.
experimentId
=
getExperimentId
();
this
.
experimentId
=
getExperimentId
();
this
.
kubernetesCRDClient
=
AdlClientFactory
.
createClient
();
this
.
configmapTemplateStr
=
fs
.
readFileSync
(
this
.
configmapTemplateStr
=
fs
.
readFileSync
(
'
./config/adl/adaptdl-nni-configmap-template.json
'
,
'
utf8
'
);
'
./config/adl/adaptdl-nni-configmap-template.json
'
,
'
utf8
'
);
this
.
jobTemplateStr
=
fs
.
readFileSync
(
'
./config/adl/adaptdljob-template.json
'
,
'
utf8
'
);
this
.
jobTemplateStr
=
fs
.
readFileSync
(
'
./config/adl/adaptdljob-template.json
'
,
'
utf8
'
);
...
@@ -294,15 +293,34 @@ python3 -m nni.tools.trial_tool.trial_keeper --trial_command '{8}' \
...
@@ -294,15 +293,34 @@ python3 -m nni.tools.trial_tool.trial_keeper --trial_command '{8}' \
return
Promise
.
resolve
(
runScript
);
return
Promise
.
resolve
(
runScript
);
}
}
public
async
cleanUp
():
Promise
<
void
>
{
super
.
cleanUp
();
// Delete Tensorboard deployment
try
{
await
this
.
genericK8sClient
.
deleteDeployment
(
"
adaptdl-tensorboard-
"
+
this
.
experimentId
.
toLowerCase
());
this
.
log
.
info
(
'
tensorboard deployment deleted
'
);
}
catch
(
error
)
{
this
.
log
.
error
(
`tensorboard deployment deletion failed:
${
error
.
message
}
`
);
}
}
public
async
setClusterMetadata
(
key
:
string
,
value
:
string
):
Promise
<
void
>
{
public
async
setClusterMetadata
(
key
:
string
,
value
:
string
):
Promise
<
void
>
{
this
.
log
.
info
(
'
SetCluster
'
+
key
+
'
,
'
+
value
);
this
.
log
.
info
(
'
SetCluster
'
+
key
+
'
,
'
+
value
);
switch
(
key
)
{
switch
(
key
)
{
case
TrialConfigMetadataKey
.
NNI_MANAGER_IP
:
case
TrialConfigMetadataKey
.
NNI_MANAGER_IP
:
this
.
nniManagerIpConfig
=
<
NNIManagerIpConfig
>
JSON
.
parse
(
value
);
this
.
nniManagerIpConfig
=
<
NNIManagerIpConfig
>
JSON
.
parse
(
value
);
break
;
break
;
case
TrialConfigMetadataKey
.
TRIAL_CONFIG
:
case
TrialConfigMetadataKey
.
TRIAL_CONFIG
:
{
this
.
adlTrialConfig
=
<
AdlTrialConfig
>
JSON
.
parse
(
value
);
this
.
adlTrialConfig
=
<
AdlTrialConfig
>
JSON
.
parse
(
value
);
let
namespace
:
string
=
'
default
'
;
if
(
this
.
adlTrialConfig
.
namespace
!==
undefined
)
{
namespace
=
this
.
adlTrialConfig
.
namespace
;
}
this
.
genericK8sClient
.
setNamespace
=
namespace
;
this
.
kubernetesCRDClient
=
AdlClientFactory
.
createClient
(
namespace
);
break
;
break
;
}
case
TrialConfigMetadataKey
.
VERSION_CHECK
:
case
TrialConfigMetadataKey
.
VERSION_CHECK
:
this
.
versionCheck
=
(
value
===
'
true
'
||
value
===
'
True
'
);
this
.
versionCheck
=
(
value
===
'
true
'
||
value
===
'
True
'
);
break
;
break
;
...
...
ts/nni_manager/training_service/kubernetes/kubernetesApiClient.ts
View file @
593a275c
...
@@ -8,17 +8,22 @@ import { Client1_10, config } from 'kubernetes-client';
...
@@ -8,17 +8,22 @@ import { Client1_10, config } from 'kubernetes-client';
import
{
getLogger
,
Logger
}
from
'
../../common/log
'
;
import
{
getLogger
,
Logger
}
from
'
../../common/log
'
;
/**
/**
* Generic
t
Kubernetes client, target version >= 1.9
* Generic Kubernetes client, target version >= 1.9
*/
*/
class
GeneralK8sClient
{
class
GeneralK8sClient
{
protected
readonly
client
:
any
;
protected
readonly
client
:
any
;
protected
readonly
log
:
Logger
=
getLogger
();
protected
readonly
log
:
Logger
=
getLogger
();
protected
namespace
:
string
=
'
default
'
;
constructor
()
{
constructor
()
{
this
.
client
=
new
Client1_10
({
config
:
config
.
fromKubeconfig
(),
version
:
'
1.9
'
});
this
.
client
=
new
Client1_10
({
config
:
config
.
fromKubeconfig
(),
version
:
'
1.9
'
});
this
.
client
.
loadSpec
();
this
.
client
.
loadSpec
();
}
}
public
set
setNamespace
(
namespace
:
string
)
{
this
.
namespace
=
namespace
;
}
private
matchStorageClass
(
response
:
any
):
string
{
private
matchStorageClass
(
response
:
any
):
string
{
const
adlSupportedProvisioners
:
RegExp
[]
=
[
const
adlSupportedProvisioners
:
RegExp
[]
=
[
new
RegExp
(
"
microk8s.io/hostpath
"
),
new
RegExp
(
"
microk8s.io/hostpath
"
),
...
@@ -60,7 +65,8 @@ class GeneralK8sClient {
...
@@ -60,7 +65,8 @@ class GeneralK8sClient {
public
async
createDeployment
(
deploymentManifest
:
any
):
Promise
<
string
>
{
public
async
createDeployment
(
deploymentManifest
:
any
):
Promise
<
string
>
{
let
result
:
Promise
<
string
>
;
let
result
:
Promise
<
string
>
;
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
'
default
'
).
deployments
.
post
({
body
:
deploymentManifest
})
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
this
.
namespace
)
.
deployments
.
post
({
body
:
deploymentManifest
})
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
response
.
body
.
metadata
.
uid
);
result
=
Promise
.
resolve
(
response
.
body
.
metadata
.
uid
);
}
else
{
}
else
{
...
@@ -72,7 +78,7 @@ class GeneralK8sClient {
...
@@ -72,7 +78,7 @@ class GeneralK8sClient {
public
async
deleteDeployment
(
deploymentName
:
string
):
Promise
<
boolean
>
{
public
async
deleteDeployment
(
deploymentName
:
string
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
let
result
:
Promise
<
boolean
>
;
// TODO: change this hard coded deployment name after demo
// TODO: change this hard coded deployment name after demo
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
'
default
'
)
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
this
.
namespace
)
.
deployment
(
deploymentName
).
delete
();
.
deployment
(
deploymentName
).
delete
();
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
result
=
Promise
.
resolve
(
true
);
...
@@ -84,7 +90,7 @@ class GeneralK8sClient {
...
@@ -84,7 +90,7 @@ class GeneralK8sClient {
public
async
createConfigMap
(
configMapManifest
:
any
):
Promise
<
boolean
>
{
public
async
createConfigMap
(
configMapManifest
:
any
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
let
result
:
Promise
<
boolean
>
;
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
)
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
)
.
configmaps
.
post
({
body
:
configMapManifest
});
.
configmaps
.
post
({
body
:
configMapManifest
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
result
=
Promise
.
resolve
(
true
);
...
@@ -97,7 +103,7 @@ class GeneralK8sClient {
...
@@ -97,7 +103,7 @@ class GeneralK8sClient {
public
async
createPersistentVolumeClaim
(
pvcManifest
:
any
):
Promise
<
boolean
>
{
public
async
createPersistentVolumeClaim
(
pvcManifest
:
any
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
let
result
:
Promise
<
boolean
>
;
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
)
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
)
.
persistentvolumeclaims
.
post
({
body
:
pvcManifest
});
.
persistentvolumeclaims
.
post
({
body
:
pvcManifest
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
result
=
Promise
.
resolve
(
true
);
...
@@ -109,8 +115,8 @@ class GeneralK8sClient {
...
@@ -109,8 +115,8 @@ class GeneralK8sClient {
public
async
createSecret
(
secretManifest
:
any
):
Promise
<
boolean
>
{
public
async
createSecret
(
secretManifest
:
any
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
let
result
:
Promise
<
boolean
>
;
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
).
secrets
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
)
.
post
({
body
:
secretManifest
});
.
secrets
.
post
({
body
:
secretManifest
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
result
=
Promise
.
resolve
(
true
);
}
else
{
}
else
{
...
...
ts/nni_manager/training_service/kubernetes/kubernetesTrainingService.ts
View file @
593a275c
...
@@ -209,13 +209,6 @@ abstract class KubernetesTrainingService {
...
@@ -209,13 +209,6 @@ abstract class KubernetesTrainingService {
return
Promise
.
reject
(
error
);
return
Promise
.
reject
(
error
);
}
}
try
{
await
this
.
genericK8sClient
.
deleteDeployment
(
"
adaptdl-tensorboard-
"
+
getExperimentId
().
toLowerCase
())
this
.
log
.
info
(
'
tensorboard deployment deleted
'
)
}
catch
(
error
)
{
this
.
log
.
error
(
`tensorboard deployment deletion failed:
${
error
.
message
}
`
)
}
return
Promise
.
resolve
();
return
Promise
.
resolve
();
}
}
...
...
ts/nni_manager/training_service/local/gpuScheduler.ts
View file @
593a275c
...
@@ -58,12 +58,12 @@ class GPUScheduler {
...
@@ -58,12 +58,12 @@ class GPUScheduler {
return
[];
return
[];
}
}
public
getSystemGpuCount
():
number
{
public
getSystemGpuCount
():
number
|
undefined
{
if
(
this
.
gpuSummary
!==
undefined
)
{
if
(
this
.
gpuSummary
!==
undefined
)
{
return
this
.
gpuSummary
.
gpuCount
;
return
this
.
gpuSummary
.
gpuCount
;
}
}
return
0
;
return
undefined
;
}
}
public
async
stop
():
Promise
<
void
>
{
public
async
stop
():
Promise
<
void
>
{
...
...
ts/nni_manager/training_service/local/localTrainingService.ts
View file @
593a275c
...
@@ -435,8 +435,8 @@ class LocalTrainingService implements TrainingService {
...
@@ -435,8 +435,8 @@ class LocalTrainingService implements TrainingService {
}
}
private
checkSpecifiedGpuIndices
():
void
{
private
checkSpecifiedGpuIndices
():
void
{
const
gpuCount
:
number
=
this
.
gpuScheduler
.
getSystemGpuCount
();
const
gpuCount
:
number
|
undefined
=
this
.
gpuScheduler
.
getSystemGpuCount
();
if
(
this
.
designatedGpuIndices
!==
undefined
)
{
if
(
this
.
designatedGpuIndices
!==
undefined
&&
gpuCount
!==
undefined
)
{
for
(
const
index
of
this
.
designatedGpuIndices
)
{
for
(
const
index
of
this
.
designatedGpuIndices
)
{
if
(
index
>=
gpuCount
)
{
if
(
index
>=
gpuCount
)
{
throw
new
Error
(
`Specified GPU index not found:
${
index
}
`
);
throw
new
Error
(
`Specified GPU index not found:
${
index
}
`
);
...
...
ts/webui/src/App.scss
View file @
593a275c
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
.headerCon
{
.headerCon
{
width
:
90%
;
width
:
90%
;
min-width
:
1200px
;
max-width
:
1490px
;
max-width
:
1490px
;
margin
:
0
auto
;
margin
:
0
auto
;
}
}
...
@@ -28,6 +29,7 @@
...
@@ -28,6 +29,7 @@
.content
{
.content
{
width
:
87%
;
width
:
87%
;
min-height
:
calc
(
100vh
-
56
);
margin
:
0
auto
;
margin
:
0
auto
;
min-width
:
1200px
;
min-width
:
1200px
;
max-width
:
1490px
;
max-width
:
1490px
;
...
...
ts/webui/src/App.tsx
View file @
593a275c
...
@@ -2,6 +2,7 @@ import * as React from 'react';
...
@@ -2,6 +2,7 @@ import * as React from 'react';
import
{
Stack
}
from
'
@fluentui/react
'
;
import
{
Stack
}
from
'
@fluentui/react
'
;
import
{
COLUMN
}
from
'
./static/const
'
;
import
{
COLUMN
}
from
'
./static/const
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
./static/datamodel
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
./static/datamodel
'
;
import
{
isManagerExperimentPage
}
from
'
./static/function
'
;
import
NavCon
from
'
./components/NavCon
'
;
import
NavCon
from
'
./components/NavCon
'
;
import
MessageInfo
from
'
./components/modals/MessageInfo
'
;
import
MessageInfo
from
'
./components/modals/MessageInfo
'
;
import
{
SlideNavBtns
}
from
'
./components/slideNav/SlideNavBtns
'
;
import
{
SlideNavBtns
}
from
'
./components/slideNav/SlideNavBtns
'
;
...
@@ -29,15 +30,15 @@ export const AppContext = React.createContext({
...
@@ -29,15 +30,15 @@ export const AppContext = React.createContext({
metricGraphMode
:
'
max
'
,
metricGraphMode
:
'
max
'
,
bestTrialEntries
:
'
10
'
,
bestTrialEntries
:
'
10
'
,
maxDurationUnit
:
'
m
'
,
maxDurationUnit
:
'
m
'
,
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeColumn
:
(
val
:
string
[])
=>
{},
changeColumn
:
(
_
val
:
string
[])
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeMetricGraphMode
:
(
val
:
'
max
'
|
'
min
'
)
=>
{},
changeMetricGraphMode
:
(
_
val
:
'
max
'
|
'
min
'
)
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeMaxDurationUnit
:
(
val
:
string
)
=>
{},
changeMaxDurationUnit
:
(
_
val
:
string
)
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeEntries
:
(
val
:
string
)
=>
{},
changeEntries
:
(
_
val
:
string
)
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage
:
()
=>
{}
updateOverviewPage
:
()
=>
{}
});
});
...
@@ -139,7 +140,10 @@ class App extends React.Component<{}, AppState> {
...
@@ -139,7 +140,10 @@ class App extends React.Component<{}, AppState> {
{
errorWhere
:
TRIALS
.
latestMetricDataError
(),
errorMessage
:
TRIALS
.
getLatestMetricDataErrorMessage
()
},
{
errorWhere
:
TRIALS
.
latestMetricDataError
(),
errorMessage
:
TRIALS
.
getLatestMetricDataErrorMessage
()
},
{
errorWhere
:
TRIALS
.
metricDataRangeError
(),
errorMessage
:
TRIALS
.
metricDataRangeErrorMessage
()
}
{
errorWhere
:
TRIALS
.
metricDataRangeError
(),
errorMessage
:
TRIALS
.
metricDataRangeErrorMessage
()
}
];
];
return
(
return
(
<
React
.
Fragment
>
{
isManagerExperimentPage
()
?
null
:
(
<
Stack
className
=
'nni'
style
=
{
{
minHeight
:
window
.
innerHeight
}
}
>
<
Stack
className
=
'nni'
style
=
{
{
minHeight
:
window
.
innerHeight
}
}
>
<
div
className
=
'header'
>
<
div
className
=
'header'
>
<
div
className
=
'headerCon'
>
<
div
className
=
'headerCon'
>
...
@@ -149,24 +153,7 @@ class App extends React.Component<{}, AppState> {
...
@@ -149,24 +153,7 @@ class App extends React.Component<{}, AppState> {
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'content'
>
<
Stack
className
=
'content'
>
{
/* search space & config */
}
{
/* search space & config */
}
<
AppContext
.
Provider
value
=
{
{
interval
,
columnList
,
changeColumn
:
this
.
changeColumn
,
experimentUpdateBroadcast
,
trialsUpdateBroadcast
,
metricGraphMode
,
maxDurationUnit
,
changeMaxDurationUnit
:
this
.
changeMaxDurationUnit
,
changeMetricGraphMode
:
this
.
changeMetricGraphMode
,
bestTrialEntries
,
changeEntries
:
this
.
changeEntries
,
updateOverviewPage
:
this
.
updateOverviewPage
}
}
>
<
SlideNavBtns
/>
<
SlideNavBtns
/>
</
AppContext
.
Provider
>
{
/* if api has error field, show error message */
}
{
/* if api has error field, show error message */
}
{
errorList
.
map
(
{
errorList
.
map
(
(
item
,
key
)
=>
(
item
,
key
)
=>
...
@@ -202,6 +189,8 @@ class App extends React.Component<{}, AppState> {
...
@@ -202,6 +189,8 @@ class App extends React.Component<{}, AppState> {
</
Stack
>
</
Stack
>
</
Stack
>
</
Stack
>
</
Stack
>
</
Stack
>
)
}
</
React
.
Fragment
>
);
);
}
}
...
...
ts/webui/src/components/NavCon.tsx
View file @
593a275c
import
*
as
React
from
'
react
'
;
import
*
as
React
from
'
react
'
;
import
axios
from
'
axios
'
;
import
axios
from
'
axios
'
;
import
{
WEBUIDOC
,
MANAGER_IP
}
from
'
../static/const
'
;
import
{
WEBUIDOC
,
MANAGER_IP
}
from
'
../static/const
'
;
import
{
import
{
Stack
,
StackItem
,
CommandBarButton
,
IContextualMenuProps
}
from
'
@fluentui/react
'
;
Stack
,
import
{
Link
}
from
'
react-router-dom
'
;
initializeIcons
,
import
{
infoIconAbout
,
timeIcon
,
disableUpdates
,
requency
,
closeTimer
,
ChevronRightMed
}
from
'
./buttons/Icon
'
;
StackItem
,
CommandBarButton
,
IContextualMenuProps
,
IStackTokens
,
IStackStyles
}
from
'
@fluentui/react
'
;
import
ExperimentSummaryPanel
from
'
./modals/ExperimentSummaryPanel
'
;
import
ExperimentSummaryPanel
from
'
./modals/ExperimentSummaryPanel
'
;
import
{
infoIconAbout
,
timeIcon
,
disableUpdates
,
requency
,
closeTimer
}
from
'
./buttons/Icon
'
;
import
{
OVERVIEWTABS
,
DETAILTABS
,
NNILOGO
}
from
'
./stateless-component/NNItabs
'
;
import
{
OVERVIEWTABS
,
DETAILTABS
,
NNILOGO
}
from
'
./stateless-component/NNItabs
'
;
import
{
EXPERIMENT
}
from
'
../static/datamodel
'
;
import
{
EXPERIMENT
}
from
'
../static/datamodel
'
;
import
{
stackTokens
,
stackStyle
}
from
'
./NavConst
'
;
import
'
../static/style/nav/nav.scss
'
;
import
'
../static/style/nav/nav.scss
'
;
import
'
../static/style/icon.scss
'
;
import
'
../static/style/icon.scss
'
;
initializeIcons
();
const
stackTokens
:
IStackTokens
=
{
childrenGap
:
15
};
const
stackStyle
:
IStackStyles
=
{
root
:
{
minWidth
:
400
,
height
:
56
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
};
interface
NavState
{
interface
NavState
{
version
:
string
;
version
:
string
;
menuVisible
:
boolean
;
menuVisible
:
boolean
;
...
@@ -133,6 +114,7 @@ class NavCon extends React.Component<NavProps, NavState> {
...
@@ -133,6 +114,7 @@ class NavCon extends React.Component<NavProps, NavState> {
};
};
return
(
return
(
<
Stack
horizontal
className
=
'nav'
>
<
Stack
horizontal
className
=
'nav'
>
<
React
.
Fragment
>
<
StackItem
grow
=
{
30
}
styles
=
{
{
root
:
{
minWidth
:
300
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
}
}
>
<
StackItem
grow
=
{
30
}
styles
=
{
{
root
:
{
minWidth
:
300
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
}
}
>
<
span
className
=
'desktop-logo'
>
{
NNILOGO
}
</
span
>
<
span
className
=
'desktop-logo'
>
{
NNILOGO
}
</
span
>
<
span
className
=
'left-right-margin'
>
{
OVERVIEWTABS
}
</
span
>
<
span
className
=
'left-right-margin'
>
{
OVERVIEWTABS
}
</
span
>
...
@@ -157,10 +139,16 @@ class NavCon extends React.Component<NavProps, NavState> {
...
@@ -157,10 +139,16 @@ class NavCon extends React.Component<NavProps, NavState> {
</
div
>
</
div
>
<
CommandBarButton
<
CommandBarButton
iconProps
=
{
{
iconName
:
'
ShowResults
'
}
}
iconProps
=
{
{
iconName
:
'
ShowResults
'
}
}
text
=
'
S
ummary'
text
=
'
Experiment s
ummary'
onClick
=
{
this
.
showExpcontent
}
onClick
=
{
this
.
showExpcontent
}
/>
/>
<
CommandBarButton
iconProps
=
{
infoIconAbout
}
text
=
'About'
menuProps
=
{
aboutProps
}
/>
<
CommandBarButton
iconProps
=
{
infoIconAbout
}
text
=
'About'
menuProps
=
{
aboutProps
}
/>
<
Link
to
=
'/experiment'
className
=
'experiment'
>
<
div
className
=
'expNavTitle'
>
<
span
>
All experiment
</
span
>
{
ChevronRightMed
}
</
div
>
</
Link
>
</
Stack
>
</
Stack
>
</
StackItem
>
</
StackItem
>
{
isvisibleExperimentDrawer
&&
(
{
isvisibleExperimentDrawer
&&
(
...
@@ -169,6 +157,7 @@ class NavCon extends React.Component<NavProps, NavState> {
...
@@ -169,6 +157,7 @@ class NavCon extends React.Component<NavProps, NavState> {
experimentProfile
=
{
EXPERIMENT
.
profile
}
experimentProfile
=
{
EXPERIMENT
.
profile
}
/>
/>
)
}
)
}
</
React
.
Fragment
>
</
Stack
>
</
Stack
>
);
);
}
}
...
...
ts/webui/src/components/NavConst.ts
0 → 100644
View file @
593a275c
import
{
IStackTokens
,
IStackStyles
}
from
'
@fluentui/react
'
;
const
stackTokens
:
IStackTokens
=
{
childrenGap
:
15
};
const
stackStyle
:
IStackStyles
=
{
root
:
{
minWidth
:
400
,
height
:
56
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
};
export
{
stackTokens
,
stackStyle
};
ts/webui/src/components/Overview.tsx
View file @
593a275c
...
@@ -6,7 +6,7 @@ import { AppContext } from '../App';
...
@@ -6,7 +6,7 @@ import { AppContext } from '../App';
import
{
Title
}
from
'
./overview/Title
'
;
import
{
Title
}
from
'
./overview/Title
'
;
import
SuccessTable
from
'
./overview/table/SuccessTable
'
;
import
SuccessTable
from
'
./overview/table/SuccessTable
'
;
import
Accuracy
from
'
./overview/Accuracy
'
;
import
Accuracy
from
'
./overview/Accuracy
'
;
import
{
Re
BasicInfo
}
from
'
./overview/
experiment
/BasicInfo
'
;
import
{
BasicInfo
}
from
'
./overview/
params
/BasicInfo
'
;
import
{
ExpDuration
}
from
'
./overview/count/ExpDuration
'
;
import
{
ExpDuration
}
from
'
./overview/count/ExpDuration
'
;
import
{
ExpDurationContext
}
from
'
./overview/count/ExpDurationContext
'
;
import
{
ExpDurationContext
}
from
'
./overview/count/ExpDurationContext
'
;
import
{
TrialCount
}
from
'
./overview/count/TrialCount
'
;
import
{
TrialCount
}
from
'
./overview/count/TrialCount
'
;
...
@@ -86,7 +86,7 @@ class Overview extends React.Component<{}, OverviewState> {
...
@@ -86,7 +86,7 @@ class Overview extends React.Component<{}, OverviewState> {
<
Title
/>
<
Title
/>
</
TitleContext
.
Provider
>
</
TitleContext
.
Provider
>
<
BestMetricContext
.
Provider
value
=
{
{
bestAccuracy
:
bestAccuracy
}
}
>
<
BestMetricContext
.
Provider
value
=
{
{
bestAccuracy
:
bestAccuracy
}
}
>
<
Re
BasicInfo
/>
<
BasicInfo
/>
</
BestMetricContext
.
Provider
>
</
BestMetricContext
.
Provider
>
</
div
>
</
div
>
{
/* duration & trial numbers */
}
{
/* duration & trial numbers */
}
...
...
ts/webui/src/components/buttons/Icon.tsx
View file @
593a275c
...
@@ -19,6 +19,9 @@ const LineChart = <Icon iconName='LineChart' />;
...
@@ -19,6 +19,9 @@ const LineChart = <Icon iconName='LineChart' />;
const
Edit
=
<
Icon
iconName
=
'Edit'
/>;
const
Edit
=
<
Icon
iconName
=
'Edit'
/>;
const
CheckMark
=
<
Icon
iconName
=
'CheckMark'
/>;
const
CheckMark
=
<
Icon
iconName
=
'CheckMark'
/>;
const
Cancel
=
<
Icon
iconName
=
'Cancel'
/>;
const
Cancel
=
<
Icon
iconName
=
'Cancel'
/>;
const
ReplyAll
=
{
iconName
:
'
ReplyAll
'
};
const
RevToggleKey
=
{
iconName
:
'
RevToggleKey
'
};
const
ChevronRightMed
=
<
Icon
iconName
=
'ChevronRightMed'
/>;
export
{
export
{
infoIcon
,
infoIcon
,
...
@@ -37,5 +40,8 @@ export {
...
@@ -37,5 +40,8 @@ export {
LineChart
,
LineChart
,
Edit
,
Edit
,
CheckMark
,
CheckMark
,
Cancel
Cancel
,
ReplyAll
,
RevToggleKey
,
ChevronRightMed
};
};
ts/webui/src/components/managementExp/ExperimentManager.tsx
0 → 100644
View file @
593a275c
import
*
as
React
from
'
react
'
;
import
{
Stack
,
DetailsList
,
DefaultButton
,
Icon
,
SearchBox
,
IColumn
}
from
'
@fluentui/react
'
;
import
{
ExperimentsManager
}
from
'
../../static/model/experimentsManager
'
;
import
{
expformatTimestamp
,
copyAndSort
}
from
'
../../static/function
'
;
import
{
AllExperimentList
,
SortInfo
}
from
'
../../static/interface
'
;
import
MessageInfo
from
'
../modals/MessageInfo
'
;
import
{
compareDate
,
filterByStatusOrPlatform
,
getSortedSource
}
from
'
./expFunction
'
;
import
{
MAXSCREENCOLUMNWIDHT
,
MINSCREENCOLUMNWIDHT
}
from
'
./experimentConst
'
;
import
{
Hearder
}
from
'
./Header
'
;
import
NameColumn
from
'
./TrialIdColumn
'
;
import
FilterBtns
from
'
./FilterBtns
'
;
import
'
../../App.scss
'
;
import
'
../../static/style/nav/nav.scss
'
;
import
'
../../static/style/experiment/experiment.scss
'
;
import
'
../../static/style/overview/probar.scss
'
;
import
'
../../static/style/tableStatus.css
'
;
interface
ExpListState
{
columns
:
IColumn
[];
platform
:
string
[];
errorMessage
:
string
;
hideFilter
:
boolean
;
searchInputVal
:
string
;
selectedStatus
:
string
;
selectedPlatform
:
string
;
selectedStartDate
?:
Date
;
selectedEndDate
?:
Date
;
sortInfo
:
SortInfo
;
source
:
AllExperimentList
[];
originExperimentList
:
AllExperimentList
[];
searchSource
:
AllExperimentList
[];
}
class
Experiment
extends
React
.
Component
<
{},
ExpListState
>
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
platform
:
[],
columns
:
this
.
columns
,
errorMessage
:
''
,
hideFilter
:
true
,
searchInputVal
:
''
,
selectedStatus
:
''
,
selectedPlatform
:
''
,
source
:
[],
// data in table
originExperimentList
:
[],
// api /experiments-info
searchSource
:
[],
// search box search result
sortInfo
:
{
field
:
''
,
isDescend
:
false
}
};
}
async
componentDidMount
():
Promise
<
void
>
{
const
EXPERIMENTMANAGER
=
new
ExperimentsManager
();
await
EXPERIMENTMANAGER
.
init
();
const
result
=
EXPERIMENTMANAGER
.
getExperimentList
();
this
.
setState
(()
=>
({
source
:
result
,
originExperimentList
:
result
,
searchSource
:
result
,
platform
:
EXPERIMENTMANAGER
.
getPlatformList
(),
errorMessage
:
EXPERIMENTMANAGER
.
getExpErrorMessage
()
}));
}
render
():
React
.
ReactNode
{
const
{
platform
,
hideFilter
,
selectedStatus
,
source
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
,
errorMessage
}
=
this
.
state
;
return
(
<
Stack
className
=
'nni'
style
=
{
{
minHeight
:
window
.
innerHeight
}
}
>
<
Hearder
/>
{
errorMessage
!==
undefined
?
(
<
div
className
=
'warning'
>
<
MessageInfo
info
=
{
errorMessage
}
typeInfo
=
'error'
/>
</
div
>
)
:
null
}
<
Stack
className
=
'contentBox expBackground'
>
<
Stack
className
=
'content'
>
<
Stack
className
=
'experimentList'
>
<
Stack
className
=
'box'
horizontal
>
<
div
className
=
'search'
>
<
SearchBox
className
=
'search-input'
placeholder
=
'Search the experiment by name and ID'
onEscape
=
{
this
.
setOriginSource
.
bind
(
this
)
}
onClear
=
{
this
.
setOriginSource
.
bind
(
this
)
}
onChange
=
{
this
.
searchNameAndId
.
bind
(
this
)
}
/>
</
div
>
<
div
className
=
'filter'
>
<
DefaultButton
onClick
=
{
this
.
clickFilter
.
bind
(
this
)
}
className
=
{
`
${
!
hideFilter
?
'
filter-button-open
'
:
null
}
`
}
>
<
Icon
iconName
=
'Equalizer'
/>
<
span
className
=
'margin'
>
Filter
</
span
>
</
DefaultButton
>
</
div
>
</
Stack
>
<
Stack
className
=
{
`
${
hideFilter
?
'
hidden
'
:
''
}
filter-condition`
}
horizontal
gap
=
{
25
}
>
<
FilterBtns
platform
=
{
platform
}
selectedStatus
=
{
selectedStatus
}
selectedPlatform
=
{
selectedPlatform
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
selectedStartDate
=
{
selectedStartDate
!
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
selectedEndDate
=
{
selectedEndDate
!
}
selectStatus
=
{
this
.
selectStatus
.
bind
(
this
)
}
selectPlatform
=
{
this
.
selectPlatform
.
bind
(
this
)
}
getSelectedData
=
{
this
.
getSelectedData
.
bind
(
this
)
}
setSearchSource
=
{
this
.
setSearchSource
.
bind
(
this
)
}
/>
</
Stack
>
<
DetailsList
columns
=
{
this
.
columns
}
items
=
{
source
}
setKey
=
'set'
compact
=
{
true
}
selectionMode
=
{
0
}
// close selector function
className
=
'table'
/>
</
Stack
>
</
Stack
>
</
Stack
>
</
Stack
>
);
}
private
onColumnClick
=
(
_ev
:
React
.
MouseEvent
<
HTMLElement
>
,
getColumn
:
IColumn
):
void
=>
{
const
{
columns
,
source
}
=
this
.
state
;
const
newColumns
:
IColumn
[]
=
columns
.
slice
();
const
currColumn
:
IColumn
=
newColumns
.
filter
(
item
=>
getColumn
.
key
===
item
.
key
)[
0
];
newColumns
.
forEach
((
newCol
:
IColumn
)
=>
{
if
(
newCol
===
currColumn
)
{
currColumn
.
isSortedDescending
=
!
currColumn
.
isSortedDescending
;
currColumn
.
isSorted
=
true
;
}
else
{
newCol
.
isSorted
=
false
;
newCol
.
isSortedDescending
=
true
;
}
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
newItems
=
copyAndSort
(
source
,
currColumn
.
fieldName
!
,
currColumn
.
isSortedDescending
);
this
.
setState
(()
=>
({
columns
:
newColumns
,
source
:
newItems
,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sortInfo
:
{
field
:
currColumn
.
fieldName
!
,
isDescend
:
currColumn
.
isSortedDescending
}
}));
};
private
columns
:
IColumn
[]
=
[
{
name
:
'
Name
'
,
key
:
'
experimentName
'
,
fieldName
:
'
experimentName
'
,
// required!
minWidth
:
MINSCREENCOLUMNWIDHT
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
<
div
className
=
'succeed-padding'
>
{
item
.
experimentName
}
</
div
>
},
{
name
:
'
ID
'
,
key
:
'
id
'
,
fieldName
:
'
id
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
,
isResizable
:
true
,
className
:
'
tableHead leftTitle
'
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
<
NameColumn
port
=
{
item
.
port
}
status
=
{
item
.
status
}
id
=
{
item
.
id
}
/>
},
{
name
:
'
Status
'
,
key
:
'
status
'
,
fieldName
:
'
status
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
,
isResizable
:
true
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
{
`
${
item
.
status
}
commonStyle succeed-padding`
}
>
{
item
.
status
}
</
div
>
)
},
{
name
:
'
Port
'
,
key
:
'
port
'
,
fieldName
:
'
port
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
-
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
-
30
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
'succeed-padding'
>
<
div
>
{
item
.
port
!==
undefined
?
item
.
port
:
'
--
'
}
</
div
>
</
div
>
)
},
{
name
:
'
Platform
'
,
key
:
'
platform
'
,
fieldName
:
'
platform
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
-
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
-
30
,
isResizable
:
true
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
<
div
className
=
'commonStyle succeed-padding'
>
{
item
.
platform
}
</
div
>
},
{
name
:
'
Start time
'
,
key
:
'
startTime
'
,
fieldName
:
'
startTime
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
+
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
+
30
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
'succeed-padding'
>
<
div
>
{
expformatTimestamp
(
item
.
startTime
)
}
</
div
>
</
div
>
)
},
{
name
:
'
End time
'
,
key
:
'
endTime
'
,
fieldName
:
'
endTime
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
+
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
+
30
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
'succeed-padding'
>
<
div
>
{
expformatTimestamp
(
item
.
endTime
)
}
</
div
>
</
div
>
)
}
];
private
clickFilter
(
_e
:
any
):
void
{
const
{
hideFilter
}
=
this
.
state
;
if
(
!
hideFilter
===
true
)
{
this
.
setSearchSource
();
}
this
.
setState
(()
=>
({
hideFilter
:
!
hideFilter
}));
}
private
setOriginSource
():
void
{
let
{
originExperimentList
}
=
this
.
state
;
const
{
sortInfo
}
=
this
.
state
;
if
(
originExperimentList
!==
undefined
)
{
originExperimentList
=
this
.
commonSelectString
(
originExperimentList
,
''
);
const
sortedData
=
getSortedSource
(
originExperimentList
,
sortInfo
);
this
.
setState
(()
=>
({
source
:
sortedData
}));
}
}
private
searchNameAndId
(
_event
,
newValue
):
void
{
const
{
originExperimentList
,
sortInfo
}
=
this
.
state
;
if
(
newValue
!==
undefined
)
{
if
(
newValue
===
''
)
{
this
.
setOriginSource
();
}
else
{
let
result
=
originExperimentList
.
filter
(
item
=>
item
.
experimentName
.
toLowerCase
().
includes
(
newValue
.
toLowerCase
())
||
item
.
id
.
toLowerCase
().
includes
(
newValue
.
toLowerCase
())
);
result
=
this
.
commonSelectString
(
result
,
''
);
const
sortedResult
=
getSortedSource
(
result
,
sortInfo
);
this
.
setState
(()
=>
({
source
:
sortedResult
,
searchSource
:
sortedResult
}));
}
this
.
setState
(()
=>
({
searchInputVal
:
newValue
}));
}
}
/***
* status, platform
* param
* data: searchSource
* field: no care selected filed
*/
private
commonSelectString
=
(
data
:
AllExperimentList
[],
field
:
string
):
AllExperimentList
[]
=>
{
const
{
selectedStatus
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
}
=
this
.
state
;
const
hasStatus
=
selectedStatus
===
''
?
false
:
true
;
const
hasPlatform
=
selectedPlatform
===
''
?
false
:
true
;
const
hasStartDate
=
selectedStartDate
===
undefined
?
false
:
true
;
const
hasEndDate
=
selectedEndDate
===
undefined
?
false
:
true
;
if
(
field
===
'
status
'
)
{
if
(
hasPlatform
)
{
data
=
filterByStatusOrPlatform
(
selectedPlatform
,
'
platform
'
,
data
);
}
}
if
(
field
===
'
platform
'
)
{
if
(
hasStatus
)
{
data
=
filterByStatusOrPlatform
(
selectedStatus
,
'
status
'
,
data
);
}
}
if
(
field
===
''
)
{
if
(
hasPlatform
)
{
data
=
filterByStatusOrPlatform
(
selectedPlatform
,
'
platform
'
,
data
);
}
if
(
hasStatus
)
{
data
=
filterByStatusOrPlatform
(
selectedStatus
,
'
status
'
,
data
);
}
}
if
(
hasStartDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
data
=
data
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
startTime
),
selectedStartDate
!
));
}
if
(
hasEndDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
data
=
data
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
endTime
),
selectedEndDate
!
));
}
return
data
;
};
// status platform startTime endTime
private
selectStatus
=
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
=>
{
if
(
item
!==
undefined
)
{
const
{
searchSource
,
sortInfo
}
=
this
.
state
;
let
result
=
filterByStatusOrPlatform
(
item
.
key
,
'
status
'
,
searchSource
);
result
=
this
.
commonSelectString
(
result
,
'
status
'
);
this
.
setState
({
selectedStatus
:
item
.
key
,
source
:
getSortedSource
(
result
,
sortInfo
)
});
}
};
private
selectPlatform
=
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
=>
{
if
(
item
!==
undefined
)
{
const
{
searchSource
,
sortInfo
}
=
this
.
state
;
let
result
=
filterByStatusOrPlatform
(
item
.
key
,
'
platform
'
,
searchSource
);
result
=
this
.
commonSelectString
(
result
,
'
platform
'
);
this
.
setState
({
selectedPlatform
:
item
.
key
,
source
:
getSortedSource
(
result
,
sortInfo
)
});
}
};
private
getSelectedData
(
type
:
string
,
date
:
Date
|
null
|
undefined
):
void
{
if
(
date
!==
null
&&
date
!==
undefined
)
{
const
{
selectedStatus
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
,
searchSource
,
sortInfo
}
=
this
.
state
;
const
hasStatus
=
selectedStatus
===
''
?
false
:
true
;
const
hasPlatform
=
selectedPlatform
===
''
?
false
:
true
;
const
hasStartDate
=
selectedStartDate
===
undefined
?
false
:
true
;
const
hasEndDate
=
selectedEndDate
===
undefined
?
false
:
true
;
let
result
;
if
(
type
===
'
start
'
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
result
=
searchSource
.
filter
(
item
=>
compareDate
(
new
Date
(
item
.
startTime
),
date
));
if
(
hasStatus
)
{
result
=
result
.
filter
(
temp
=>
temp
.
status
===
selectedStatus
);
}
if
(
hasPlatform
)
{
result
=
result
.
filter
(
temp
=>
temp
.
platform
===
selectedPlatform
);
}
if
(
hasEndDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
result
=
result
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
endTime
),
selectedEndDate
!
));
}
this
.
setState
(()
=>
({
source
:
getSortedSource
(
result
,
sortInfo
),
selectedStartDate
:
date
}));
}
else
{
result
=
searchSource
.
filter
(
item
=>
compareDate
(
new
Date
(
item
.
endTime
),
date
));
if
(
hasStatus
)
{
result
=
result
.
filter
(
temp
=>
temp
.
status
===
selectedStatus
);
}
if
(
hasPlatform
)
{
result
=
result
.
filter
(
temp
=>
temp
.
platform
===
selectedPlatform
);
}
if
(
hasStartDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
result
=
result
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
startTime
),
selectedStartDate
!
));
}
this
.
setState
(()
=>
({
source
:
getSortedSource
(
result
,
sortInfo
),
selectedEndDate
:
date
}));
}
}
}
// reset
private
setSearchSource
():
void
{
const
{
sortInfo
,
searchInputVal
,
originExperimentList
}
=
this
.
state
;
// hert re-search data for fix this status: filter first -> searchBox search result null -> close filter
const
result
=
originExperimentList
.
filter
(
item
=>
item
.
experimentName
.
toLowerCase
().
includes
(
searchInputVal
.
toLowerCase
())
||
item
.
id
.
toLowerCase
().
includes
(
searchInputVal
.
toLowerCase
())
);
this
.
setState
(()
=>
({
source
:
getSortedSource
(
result
,
sortInfo
),
selectedStatus
:
''
,
selectedPlatform
:
''
,
selectedStartDate
:
undefined
,
selectedEndDate
:
undefined
}));
}
}
export
default
Experiment
;
ts/webui/src/components/managementExp/FilterBtns.tsx
0 → 100644
View file @
593a275c
import
*
as
React
from
'
react
'
;
import
{
DefaultButton
,
Icon
,
Dropdown
,
DatePicker
,
DayOfWeek
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENTSTATUS
}
from
'
../../static/const
'
;
import
{
fillOptions
}
from
'
./expFunction
'
;
interface
FilterBtnsProps
{
platform
:
string
[];
selectedStatus
:
string
;
selectedPlatform
:
string
;
selectedStartDate
:
Date
;
selectedEndDate
:
Date
;
selectStatus
:
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
)
=>
void
;
selectPlatform
:
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
)
=>
void
;
getSelectedData
:
(
type
:
string
,
date
:
Date
|
null
|
undefined
)
=>
void
;
setSearchSource
:
()
=>
void
;
}
class
FilterBtns
extends
React
.
Component
<
FilterBtnsProps
,
{}
>
{
constructor
(
props
:
FilterBtnsProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
{
platform
,
selectedStatus
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
,
selectStatus
,
selectPlatform
,
getSelectedData
,
setSearchSource
}
=
this
.
props
;
return
(
<
React
.
Fragment
>
<
Dropdown
label
=
'Status'
selectedKey
=
{
selectedStatus
}
onChange
=
{
selectStatus
.
bind
(
this
)
}
placeholder
=
'Select an option'
options
=
{
fillOptions
(
EXPERIMENTSTATUS
)
}
className
=
'filter-condition-status'
/>
<
Dropdown
label
=
'Platform'
selectedKey
=
{
selectedPlatform
}
onChange
=
{
selectPlatform
.
bind
(
this
)
}
placeholder
=
'Select an option'
options
=
{
fillOptions
(
platform
)
}
className
=
'filter-condition-platform'
/>
<
DatePicker
label
=
'Start time'
firstDayOfWeek
=
{
DayOfWeek
.
Sunday
}
showMonthPickerAsOverlay
=
{
true
}
placeholder
=
'Select a date...'
ariaLabel
=
'Select a date'
value
=
{
selectedStartDate
}
onSelectDate
=
{
getSelectedData
.
bind
(
this
,
'
start
'
)
}
/>
<
DatePicker
label
=
'End time'
firstDayOfWeek
=
{
DayOfWeek
.
Sunday
}
showMonthPickerAsOverlay
=
{
true
}
placeholder
=
'Select a date...'
ariaLabel
=
'Select a date'
value
=
{
selectedEndDate
}
onSelectDate
=
{
getSelectedData
.
bind
(
this
,
'
end
'
)
}
/>
<
DefaultButton
onClick
=
{
setSearchSource
.
bind
(
this
)
}
className
=
'reset'
>
<
Icon
iconName
=
'Refresh'
/>
<
span
className
=
'margin'
>
Reset
</
span
>
</
DefaultButton
>
</
React
.
Fragment
>
);
}
}
export
default
FilterBtns
;
ts/webui/src/components/managementExp/Header.tsx
0 → 100644
View file @
593a275c
import
React
from
'
react
'
;
import
{
Link
}
from
'
react-router-dom
'
;
import
{
Stack
,
StackItem
,
CommandBarButton
}
from
'
@fluentui/react
'
;
import
{
RevToggleKey
}
from
'
../buttons/Icon
'
;
import
{
NNILOGO
}
from
'
../stateless-component/NNItabs
'
;
import
{
stackTokens
,
stackStyle
}
from
'
../NavConst
'
;
export
const
Hearder
=
():
any
=>
(
<
div
className
=
'header'
>
<
div
className
=
'headerCon'
>
<
Stack
className
=
'nav'
horizontal
>
<
StackItem
grow
=
{
30
}
styles
=
{
{
root
:
{
minWidth
:
300
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
}
}
>
<
span
className
=
'desktop-logo'
>
{
NNILOGO
}
</
span
>
<
span
className
=
'logoTitle'
>
Neural Network Intelligence
</
span
>
</
StackItem
>
<
StackItem
grow
=
{
70
}
className
=
'navOptions'
>
<
Stack
horizontal
horizontalAlign
=
'end'
tokens
=
{
stackTokens
}
styles
=
{
stackStyle
}
>
<
Link
to
=
'/oview'
className
=
'experiment'
>
<
CommandBarButton
iconProps
=
{
RevToggleKey
}
text
=
'Back to the experiment'
/>
</
Link
>
</
Stack
>
</
StackItem
>
</
Stack
>
</
div
>
</
div
>
);
ts/webui/src/components/managementExp/TrialIdColumn.tsx
0 → 100644
View file @
593a275c
import
*
as
React
from
'
react
'
;
interface
TrialIdColumnProps
{
port
:
number
;
id
:
string
;
status
:
string
;
}
class
TrialIdColumn
extends
React
.
Component
<
TrialIdColumnProps
,
{}
>
{
constructor
(
props
:
TrialIdColumnProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
{
port
,
id
,
status
}
=
this
.
props
;
const
hostname
=
window
.
location
.
hostname
;
const
protocol
=
window
.
location
.
protocol
;
const
webuiPortal
=
`
${
protocol
}
//
${
hostname
}
:
${
port
}
/oview`
;
return
(
<
div
className
=
'succeed-padding ellipsis'
>
{
status
===
'
STOPPED
'
?
(
<
div
>
{
id
}
</
div
>
)
:
(
<
a
href
=
{
webuiPortal
}
className
=
'link'
target
=
'_blank'
rel
=
'noopener noreferrer'
>
{
id
}
</
a
>
)
}
</
div
>
);
}
}
export
default
TrialIdColumn
;
Prev
1
2
3
4
5
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment