Unverified Commit 9fae194a authored by SparkSnail's avatar SparkSnail Committed by GitHub
Browse files

Merge pull request #206 from microsoft/master

merge master
parents 8fe2588b 41e58703
...@@ -32,7 +32,7 @@ def classic_mode( ...@@ -32,7 +32,7 @@ def classic_mode(
'''Execute the chosen function and inputs directly. '''Execute the chosen function and inputs directly.
In this mode, the trial code is only running the chosen subgraph (i.e., the chosen ops and inputs), In this mode, the trial code is only running the chosen subgraph (i.e., the chosen ops and inputs),
without touching the full model graph.''' without touching the full model graph.'''
if trial._params is None: if trial.get_current_parameter() is None:
trial.get_next_parameter() trial.get_next_parameter()
mutable_block = trial.get_current_parameter(mutable_id) mutable_block = trial.get_current_parameter(mutable_id)
chosen_layer = mutable_block[mutable_layer_id]["chosen_layer"] chosen_layer = mutable_block[mutable_layer_id]["chosen_layer"]
...@@ -118,7 +118,7 @@ def oneshot_mode( ...@@ -118,7 +118,7 @@ def oneshot_mode(
The difference is that oneshot mode does not receive subgraph. The difference is that oneshot mode does not receive subgraph.
Instead, it uses dropout to randomly dropout inputs and ops.''' Instead, it uses dropout to randomly dropout inputs and ops.'''
# NNI requires to get_next_parameter before report a result. But the parameter will not be used in this mode # NNI requires to get_next_parameter before report a result. But the parameter will not be used in this mode
if trial._params is None: if trial.get_current_parameter() is None:
trial.get_next_parameter() trial.get_next_parameter()
optional_inputs = list(optional_inputs.values()) optional_inputs = list(optional_inputs.values())
inputs_num = len(optional_inputs) inputs_num = len(optional_inputs)
......
...@@ -189,6 +189,6 @@ else: ...@@ -189,6 +189,6 @@ else:
raise RuntimeError('Unrecognized mode: %s' % mode) raise RuntimeError('Unrecognized mode: %s' % mode)
def _get_param(key): def _get_param(key):
if trial._params is None: if trial.get_current_parameter() is None:
trial.get_next_parameter() trial.get_next_parameter()
return trial.get_current_parameter(key) return trial.get_current_parameter(key)
...@@ -50,10 +50,12 @@ def get_next_parameter(): ...@@ -50,10 +50,12 @@ def get_next_parameter():
return None return None
return _params['parameters'] return _params['parameters']
def get_current_parameter(tag): def get_current_parameter(tag=None):
global _params global _params
if _params is None: if _params is None:
return None return None
if tag is None:
return _params['parameters']
return _params['parameters'][tag] return _params['parameters'][tag]
def get_experiment_id(): def get_experiment_id():
......
...@@ -57,19 +57,22 @@ class Tuner(Recoverable): ...@@ -57,19 +57,22 @@ class Tuner(Recoverable):
def receive_trial_result(self, parameter_id, parameters, value, **kwargs): def receive_trial_result(self, parameter_id, parameters, value, **kwargs):
"""Invoked when a trial reports its final result. Must override. """Invoked when a trial reports its final result. Must override.
By default this only reports results of algorithm-generated hyper-parameters.
Use `accept_customized_trials()` to receive results from user-added parameters.
parameter_id: int parameter_id: int
parameters: object created by 'generate_parameters()' parameters: object created by 'generate_parameters()'
reward: object reported by trial value: object reported by trial
customized: bool, true if the trial is created from web UI, false if generated by algorithm
trial_job_id: str, only available in multiphase mode.
""" """
raise NotImplementedError('Tuner: receive_trial_result not implemented') raise NotImplementedError('Tuner: receive_trial_result not implemented')
def receive_customized_trial_result(self, parameter_id, parameters, value, **kwargs): def accept_customized_trials(self, accept=True):
"""Invoked when a trial added by WebUI reports its final result. Do nothing by default. """Enable or disable receiving results of user-added hyper-parameters.
parameter_id: int By default `receive_trial_result()` will only receive results of algorithm-generated hyper-parameters.
parameters: object created by user If tuners want to receive those of customized parameters as well, they can call this function in `__init__()`.
value: object reported by trial
""" """
_logger.info('Customized trial job %s ignored by tuner', parameter_id) self._accept_customized = accept
def trial_end(self, parameter_id, success, **kwargs): def trial_end(self, parameter_id, success, **kwargs):
"""Invoked when a trial is completed or terminated. Do nothing by default. """Invoked when a trial is completed or terminated. Do nothing by default.
......
...@@ -34,6 +34,7 @@ class NaiveTuner(Tuner): ...@@ -34,6 +34,7 @@ class NaiveTuner(Tuner):
self.param = 0 self.param = 0
self.trial_results = [ ] self.trial_results = [ ]
self.search_space = None self.search_space = None
self.accept_customized_trials()
def generate_parameters(self, parameter_id, **kwargs): def generate_parameters(self, parameter_id, **kwargs):
# report Tuner's internal states to generated parameters, # report Tuner's internal states to generated parameters,
...@@ -45,13 +46,9 @@ class NaiveTuner(Tuner): ...@@ -45,13 +46,9 @@ class NaiveTuner(Tuner):
'search_space': self.search_space 'search_space': self.search_space
} }
def receive_trial_result(self, parameter_id, parameters, value, **kwargs): def receive_trial_result(self, parameter_id, parameters, value, customized, **kwargs):
reward = extract_scalar_reward(value) reward = extract_scalar_reward(value)
self.trial_results.append((parameter_id, parameters['param'], reward, False)) self.trial_results.append((parameter_id, parameters['param'], reward, customized))
def receive_customized_trial_result(self, parameter_id, parameters, value):
reward = extract_scalar_reward(value)
self.trial_results.append((parameter_id, parameters['param'], reward, True))
def update_search_space(self, search_space): def update_search_space(self, search_space):
self.search_space = search_space self.search_space = search_space
......
...@@ -107,6 +107,7 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -107,6 +107,7 @@ class Compare extends React.Component<CompareProps, {}> {
initColumn = () => { initColumn = () => {
const { compareRows } = this.props; const { compareRows } = this.props;
const idList: Array<string> = []; const idList: Array<string> = [];
const sequenceIdList: Array<number> = [];
const durationList: Array<number> = []; const durationList: Array<number> = [];
const parameterList: Array<object> = []; const parameterList: Array<object> = [];
...@@ -117,6 +118,7 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -117,6 +118,7 @@ class Compare extends React.Component<CompareProps, {}> {
Object.keys(compareRows).map(item => { Object.keys(compareRows).map(item => {
const temp = compareRows[item]; const temp = compareRows[item];
idList.push(temp.id); idList.push(temp.id);
sequenceIdList.push(temp.sequenceId);
durationList.push(temp.duration); durationList.push(temp.duration);
parameterList.push(temp.description.parameters); parameterList.push(temp.description.parameters);
}); });
...@@ -124,13 +126,21 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -124,13 +126,21 @@ class Compare extends React.Component<CompareProps, {}> {
<table className="compare"> <table className="compare">
<tbody> <tbody>
<tr> <tr>
<td /> <td className="column">Id</td>
{Object.keys(idList).map(key => { {Object.keys(idList).map(key => {
return ( return (
<td className="value idList" key={key}>{idList[key]}</td> <td className="value idList" key={key}>{idList[key]}</td>
); );
})} })}
</tr> </tr>
<tr>
<td className="column">Trial No.</td>
{Object.keys(sequenceIdList).map(key => {
return (
<td className="value idList" key={key}>{sequenceIdList[key]}</td>
);
})}
</tr>
<tr> <tr>
<td className="column">Default metric</td> <td className="column">Default metric</td>
{Object.keys(compareRows).map(index => { {Object.keys(compareRows).map(index => {
......
...@@ -223,6 +223,16 @@ class SlideBar extends React.Component<SliderProps, SliderState> { ...@@ -223,6 +223,16 @@ class SlideBar extends React.Component<SliderProps, SliderState> {
<span>{DETAILTABS}</span> <span>{DETAILTABS}</span>
</Col> </Col>
<Col span={16} className="desktop-right"> <Col span={16} className="desktop-right">
<span>
<Button
className="fresh"
type="ghost"
>
<a target="_blank" href="https://nni.readthedocs.io/en/latest/Tutorial/WebUI.html">
<Icon type="question" /><span>Help</span>
</a>
</Button>
</span>
<span>{this.select()}</span> <span>{this.select()}</span>
<span> <span>
<Dropdown <Dropdown
...@@ -238,9 +248,9 @@ class SlideBar extends React.Component<SliderProps, SliderState> { ...@@ -238,9 +248,9 @@ class SlideBar extends React.Component<SliderProps, SliderState> {
{ {
menuVisible menuVisible
? ?
<Icon type="up" className="margin-icon"/> <Icon type="up" className="margin-icon" />
: :
<Icon type="down" className="margin-icon"/> <Icon type="down" className="margin-icon" />
} }
</a> </a>
</Dropdown> </Dropdown>
......
...@@ -171,7 +171,9 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState> ...@@ -171,7 +171,9 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
status: status, status: status,
duration: duration, duration: duration,
acc: acc, acc: acc,
description: desc description: desc,
startTime: begin,
endTime: (end !== undefined) ? end : undefined
}); });
}); });
// update search data result // update search data result
......
...@@ -85,7 +85,9 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -85,7 +85,9 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
}); });
// deal with best metric line // deal with best metric line
const bestCurve: Array<number | object>[] = []; // best curve data source const bestCurve: Array<number | object>[] = []; // best curve data source
if (lineListDefault[0] !== undefined) {
bestCurve.push([lineListDefault[0][0], lineListDefault[0][1], accSource[0].searchSpace]); bestCurve.push([lineListDefault[0][0], lineListDefault[0][1], accSource[0].searchSpace]);
}
if (optimize === 'maximize') { if (optimize === 'maximize') {
for (let i = 1; i < lineListDefault.length; i++) { for (let i = 1; i < lineListDefault.length; i++) {
const val = lineListDefault[i][1]; const val = lineListDefault[i][1];
......
...@@ -128,13 +128,22 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -128,13 +128,22 @@ class Para extends React.Component<ParaProps, ParaState> {
hyperParaPic = (source: Array<TableObj>, searchSpace: string) => { hyperParaPic = (source: Array<TableObj>, searchSpace: string) => {
// filter succeed trials [{}, {}, {}] // filter succeed trials [{}, {}, {}]
const dataSource: Array<TableObj> = source.filter(filterByStatus); const origin = source.filter(filterByStatus);
const dataSource: Array<TableObj> = JSON.parse(JSON.stringify(origin));
const lenOfDataSource: number = dataSource.length; const lenOfDataSource: number = dataSource.length;
const accPara: Array<number> = []; const accPara: Array<number> = [];
// specific value array // specific value array
const eachTrialParams: Array<string> = []; const eachTrialParams: Array<string> = [];
// experiment interface search space obj // experiment interface search space obj
const searchRange = searchSpace !== undefined ? JSON.parse(searchSpace) : ''; const searchRange = searchSpace !== undefined ? JSON.parse(searchSpace) : '';
// nest search space
let isNested: boolean = false;
Object.keys(searchRange).map(item => {
if (typeof searchRange[item]._value[0] === 'object') {
isNested = true;
return;
}
});
const dimName = Object.keys(searchRange); const dimName = Object.keys(searchRange);
if (this._isMounted === true) { if (this._isMounted === true) {
this.setState(() => ({ dimName: dimName })); this.setState(() => ({ dimName: dimName }));
...@@ -143,6 +152,7 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -143,6 +152,7 @@ class Para extends React.Component<ParaProps, ParaState> {
const parallelAxis: Array<Dimobj> = []; const parallelAxis: Array<Dimobj> = [];
// search space range and specific value [only number] // search space range and specific value [only number]
let i = 0; let i = 0;
if (isNested === false) {
for (i; i < dimName.length; i++) { for (i; i < dimName.length; i++) {
const searchKey = searchRange[dimName[i]]; const searchKey = searchRange[dimName[i]];
switch (searchKey._type) { switch (searchKey._type) {
...@@ -155,7 +165,6 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -155,7 +165,6 @@ class Para extends React.Component<ParaProps, ParaState> {
min: searchKey._value[0] min: searchKey._value[0]
}); });
break; break;
case 'randint': case 'randint':
parallelAxis.push({ parallelAxis.push({
dim: i, dim: i,
...@@ -164,7 +173,6 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -164,7 +173,6 @@ class Para extends React.Component<ParaProps, ParaState> {
max: searchKey._value[1], max: searchKey._value[1],
}); });
break; break;
case 'choice': case 'choice':
const data: Array<string> = []; const data: Array<string> = [];
for (let j = 0; j < searchKey._value.length; j++) { for (let j = 0; j < searchKey._value.length; j++) {
...@@ -209,13 +217,66 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -209,13 +217,66 @@ class Para extends React.Component<ParaProps, ParaState> {
}); });
} }
break; break;
default: default:
parallelAxis.push({ parallelAxis.push({
dim: i, dim: i,
name: dimName[i] name: dimName[i]
}); });
}
}
} else {
for (i; i < dimName.length; i++) {
const searchKey = searchRange[dimName[i]];
switch (searchKey._type) {
case 'choice':
const data: Array<string> = [];
let j = 0;
for (j; j < searchKey._value.length; j++) {
const item = searchKey._value[j];
Object.keys(item).map(key => {
if (key !== '_name' && key !== '_type') {
Object.keys(item[key]).map(index => {
if (index !== '_type') {
const realChoice = item[key][index];
Object.keys(realChoice).map(m => {
data.push(`${item._name}_${realChoice[m]}`);
});
}
});
}
});
}
data.push('null');
parallelAxis.push({
dim: i,
name: dimName[i],
type: 'category',
data: data,
boundaryGap: true,
axisLine: {
lineStyle: {
type: 'dotted', // axis type,solid dashed dotted
width: 1
}
},
axisTick: {
show: true,
interval: 0,
alignWithLabel: true,
},
axisLabel: {
show: true,
interval: 0,
// rotate: 30
},
});
break;
default:
parallelAxis.push({
dim: i,
name: dimName[i]
});
}
} }
} }
parallelAxis.push({ parallelAxis.push({
...@@ -283,6 +344,23 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -283,6 +344,23 @@ class Para extends React.Component<ParaProps, ParaState> {
} }
} }
}); });
// nested search space, deal data
if (isNested !== false) {
eachTrialParams.forEach(element => {
Object.keys(element).forEach(key => {
let item = element[key];
if (typeof item === 'object') {
Object.keys(item).forEach(index => {
if (index !== '_name') {
element[key] = `${item._name}_${item[index]}`;
} else {
element[key] = 'null';
}
});
}
});
});
}
if (this._isMounted) { if (this._isMounted) {
// if not return final result // if not return final result
const maxVal = accPara.length === 0 ? 1 : Math.max(...accPara); const maxVal = accPara.length === 0 ? 1 : Math.max(...accPara);
......
...@@ -182,7 +182,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -182,7 +182,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
// checkbox for coloumn // checkbox for coloumn
selectedColumn = (checkedValues: Array<string>) => { selectedColumn = (checkedValues: Array<string>) => {
// 7: because have seven common column, "Intermediate count" is not shown by default // 7: because have seven common column, "Intermediate count" is hidden by default
let count = 7; let count = 7;
const want: Array<object> = []; const want: Array<object> = [];
const finalKeys: Array<string> = []; const finalKeys: Array<string> = [];
...@@ -191,6 +191,8 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -191,6 +191,8 @@ class TableList extends React.Component<TableListProps, TableListState> {
switch (checkedValues[m]) { switch (checkedValues[m]) {
case 'Trial No.': case 'Trial No.':
case 'ID': case 'ID':
case 'StartTime':
case 'EndTime':
case 'Duration': case 'Duration':
case 'Status': case 'Status':
case 'Operation': case 'Operation':
...@@ -347,6 +349,50 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -347,6 +349,50 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
}); });
break; break;
case 'StartTime':
showColumn.push({
title: 'StartTime',
dataIndex: 'startTime',
key: 'startTime',
width: 160,
render: (text: string, record: TableObj) => {
const start = record.startTime !== undefined ? record.startTime : -1;
return (
<span>
{
start !== -1
?
new Date(start).toLocaleString('en-US')
:
'--'
}
</span>
);
},
});
break;
case 'EndTime':
showColumn.push({
title: 'EndTime',
dataIndex: 'endTime',
key: 'endTime',
width: 160,
render: (text: string, record: TableObj) => {
const end = record.endTime !== undefined ? record.endTime : -1;
return (
<span>
{
end !== -1
?
new Date(end).toLocaleString('en-US')
:
'--'
}
</span>
);
},
});
break;
case 'Duration': case 'Duration':
showColumn.push({ showColumn.push({
title: 'Duration', title: 'Duration',
......
...@@ -34,21 +34,29 @@ const COLUMN_INDEX = [ ...@@ -34,21 +34,29 @@ const COLUMN_INDEX = [
index: 2 index: 2
}, },
{ {
name: 'Duration', name: 'StartTime',
index: 3 index: 3
}, },
{ {
name: 'Status', name: 'EndTime',
index: 4 index: 4
}, },
{ {
name: 'Intermediate count', name: 'Duration',
index: 5 index: 5
}, },
{ {
name: 'Default', name: 'Status',
index: 6 index: 6
}, },
{
name: 'Intermediate count',
index: 7
},
{
name: 'Default',
index: 8
},
{ {
name: 'Operation', name: 'Operation',
index: 10000 index: 10000
...@@ -57,7 +65,8 @@ const COLUMN_INDEX = [ ...@@ -57,7 +65,8 @@ const COLUMN_INDEX = [
// defatult selected column // defatult selected column
const COLUMN = ['Trial No.', 'ID', 'Duration', 'Status', 'Default', 'Operation']; const COLUMN = ['Trial No.', 'ID', 'Duration', 'Status', 'Default', 'Operation'];
// all choice column !dictory final // all choice column !dictory final
const COLUMNPro = ['Trial No.', 'ID', 'Duration', 'Status', 'Intermediate count', 'Default', 'Operation']; const COLUMNPro = ['Trial No.', 'ID', 'StartTime', 'EndTime', 'Duration', 'Status',
'Intermediate count', 'Default', 'Operation'];
export { export {
MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMNPro, MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMNPro,
CONTROLTYPE, MONACO, COLUMN, COLUMN_INDEX, DRAWEROPTION CONTROLTYPE, MONACO, COLUMN, COLUMN_INDEX, DRAWEROPTION
......
...@@ -8,6 +8,8 @@ interface TableObj { ...@@ -8,6 +8,8 @@ interface TableObj {
acc?: FinalType; // draw accuracy graph acc?: FinalType; // draw accuracy graph
description: Parameters; description: Parameters;
color?: string; color?: string;
startTime?: number;
endTime?: number;
} }
interface SearchSpace { interface SearchSpace {
......
...@@ -9,10 +9,11 @@ $drowHoverBgColor: #e2e2e2; ...@@ -9,10 +9,11 @@ $drowHoverBgColor: #e2e2e2;
border: none; border: none;
color: #fff; color: #fff;
font-size: 16px; font-size: 16px;
padding: 0; padding: 0 8px;
} }
.fresh:hover{ .fresh:hover{
color: #fff; color: #fff;
background-color: #006cb5;
} }
.dropdown{ .dropdown{
......
...@@ -115,6 +115,7 @@ ...@@ -115,6 +115,7 @@
} }
#detail-button{ #detail-button{
margin: 2px 0;
.common-style, .common-style:visited, .common-style:focus{ .common-style, .common-style:visited, .common-style:focus{
height: 26px; height: 26px;
border: none; border: none;
...@@ -131,7 +132,7 @@ ...@@ -131,7 +132,7 @@
.common-style:disabled{ .common-style:disabled{
background-color: #f4f4f4; background-color: #f4f4f4;
} }
.special, .special:visited, .special:focus{ .special, .special:visited, .special:focus, .special button{
height: 26px; height: 26px;
border: none; border: none;
border-radius: 0; border-radius: 0;
...@@ -146,7 +147,7 @@ ...@@ -146,7 +147,7 @@
background-color: #c8c8c8; background-color: #c8c8c8;
outline: 0; outline: 0;
} }
.special:disabled{ .special:disabled, .special button:disabled{
background-color: #f4f4f4; background-color: #f4f4f4;
color: #d9d9d9; color: #d9d9d9;
} }
......
authorName: nni
experimentName: default_test
maxExecDuration: 15m
maxTrialNum: 4
trialConcurrency: 2
searchSpacePath: ./mnist_pytorch_search_space.json
tuner:
builtinTunerName: Random
assessor:
builtinAssessorName: Medianstop
classArgs:
optimize_mode: maximize
trial:
codeDir: ../../../examples/trials/mnist-pytorch
command: python3 mnist.py --epochs 2
gpuNum: 0
useAnnotation: false
multiPhase: false
multiThread: false
trainingServicePlatform: local
{
"batch_size": {"_type":"choice", "_value": [16, 32, 64, 128]},
"hidden_size":{"_type":"choice","_value":[128, 256, 512, 1024]},
"lr":{"_type":"choice","_value":[0.0001, 0.001, 0.01, 0.1]},
"momentum":{"_type":"uniform","_value":[0, 1]}
}
...@@ -240,7 +240,12 @@ pai_trial_schema = { ...@@ -240,7 +240,12 @@ pai_trial_schema = {
Optional('outputDir'): And(Regex(r'hdfs://(([0-9]{1,3}.){3}[0-9]{1,3})(:[0-9]{2,5})?(/.*)?'),\ Optional('outputDir'): And(Regex(r'hdfs://(([0-9]{1,3}.){3}[0-9]{1,3})(:[0-9]{2,5})?(/.*)?'),\
error='ERROR: outputDir format error, outputDir format is hdfs://xxx.xxx.xxx.xxx:xxx'), error='ERROR: outputDir format error, outputDir format is hdfs://xxx.xxx.xxx.xxx:xxx'),
Optional('virtualCluster'): setType('virtualCluster', str), Optional('virtualCluster'): setType('virtualCluster', str),
Optional('nasMode'): setChoice('nasMode', 'classic_mode', 'enas_mode', 'oneshot_mode', 'darts_mode') Optional('nasMode'): setChoice('nasMode', 'classic_mode', 'enas_mode', 'oneshot_mode', 'darts_mode'),
Optional('portList'): [{
"label": setType('label', str),
"beginAt": setType('beginAt', int),
"portNumber": setType('portNumber', int)
}]
} }
} }
...@@ -310,7 +315,8 @@ kubeflow_config_schema = { ...@@ -310,7 +315,8 @@ kubeflow_config_schema = {
error='ERROR: accountName format error, accountName support using (0-9|a-z|A-Z|-)'), error='ERROR: accountName format error, accountName support using (0-9|a-z|A-Z|-)'),
'azureShare': And(Regex('([0-9]|[a-z]|[A-Z]|-){3,63}'),\ 'azureShare': And(Regex('([0-9]|[a-z]|[A-Z]|-){3,63}'),\
error='ERROR: azureShare format error, azureShare support using (0-9|a-z|A-Z|-)') error='ERROR: azureShare format error, azureShare support using (0-9|a-z|A-Z|-)')
} },
Optional('uploadRetryCount'): setNumberRange('uploadRetryCount', int, 1, 99999)
}) })
} }
...@@ -356,7 +362,8 @@ frameworkcontroller_config_schema = { ...@@ -356,7 +362,8 @@ frameworkcontroller_config_schema = {
error='ERROR: accountName format error, accountName support using (0-9|a-z|A-Z|-)'), error='ERROR: accountName format error, accountName support using (0-9|a-z|A-Z|-)'),
'azureShare': And(Regex('([0-9]|[a-z]|[A-Z]|-){3,63}'),\ 'azureShare': And(Regex('([0-9]|[a-z]|[A-Z]|-){3,63}'),\
error='ERROR: azureShare format error, azureShare support using (0-9|a-z|A-Z|-)') error='ERROR: azureShare format error, azureShare support using (0-9|a-z|A-Z|-)')
} },
Optional('uploadRetryCount'): setNumberRange('uploadRetryCount', int, 1, 99999)
}) })
} }
......
...@@ -198,10 +198,7 @@ def validate_common_content(experiment_config): ...@@ -198,10 +198,7 @@ def validate_common_content(experiment_config):
Schema({**separate_schema_dict[separate_key]['customized']}).validate(experiment_config[separate_key]) Schema({**separate_schema_dict[separate_key]['customized']}).validate(experiment_config[separate_key])
except SchemaError as error: except SchemaError as error:
print_error('Your config file is not correct, please check your config file content!') print_error('Your config file is not correct, please check your config file content!')
if error.__str__().__contains__('Wrong key'): print_error(error.code)
print_error(' '.join(error.__str__().split()[:3]))
else:
print_error(error)
exit(1) exit(1)
#set default value #set default value
......
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