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

Merge pull request #113 from Microsoft/master

merge master
parents 85cb472e c288a16e
...@@ -66,8 +66,7 @@ def init_logger(logger_file_path): ...@@ -66,8 +66,7 @@ def init_logger(logger_file_path):
elif env_args.log_dir is not None: elif env_args.log_dir is not None:
logger_file_path = os.path.join(env_args.log_dir, logger_file_path) logger_file_path = os.path.join(env_args.log_dir, logger_file_path)
logger_file = open(logger_file_path, 'w') logger_file = open(logger_file_path, 'w')
fmt = '[%(asctime)s] %(levelname)s (%(name)s/%(threadName)s) %(message)s'
fmt = '[%(asctime)s] %(levelname)s (%(name)s) %(message)s'
formatter = logging.Formatter(fmt, _time_format) formatter = logging.Formatter(fmt, _time_format)
handler = logging.StreamHandler(logger_file) handler = logging.StreamHandler(logger_file)
......
...@@ -97,6 +97,7 @@ class MsgDispatcher(MsgDispatcherBase): ...@@ -97,6 +97,7 @@ class MsgDispatcher(MsgDispatcherBase):
def handle_request_trial_jobs(self, data): def handle_request_trial_jobs(self, data):
# data: number or trial jobs # data: number or trial jobs
ids = [_create_parameter_id() for _ in range(data)] ids = [_create_parameter_id() for _ in range(data)]
_logger.debug("requesting for generating params of {}".format(ids))
params_list = self.tuner.generate_multiple_parameters(ids) params_list = self.tuner.generate_multiple_parameters(ids)
for i, _ in enumerate(params_list): for i, _ in enumerate(params_list):
......
...@@ -19,10 +19,14 @@ ...@@ -19,10 +19,14 @@
# ================================================================================================== # ==================================================================================================
#import json_tricks #import json_tricks
import os
import logging import logging
import json_tricks import os
from queue import Queue
import sys
from multiprocessing.dummy import Pool as ThreadPool from multiprocessing.dummy import Pool as ThreadPool
import json_tricks
from .common import init_logger, multi_thread_enabled from .common import init_logger, multi_thread_enabled
from .recoverable import Recoverable from .recoverable import Recoverable
from .protocol import CommandType, receive from .protocol import CommandType, receive
...@@ -34,6 +38,7 @@ class MsgDispatcherBase(Recoverable): ...@@ -34,6 +38,7 @@ class MsgDispatcherBase(Recoverable):
def __init__(self): def __init__(self):
if multi_thread_enabled(): if multi_thread_enabled():
self.pool = ThreadPool() self.pool = ThreadPool()
self.thread_results = []
def run(self): def run(self):
"""Run the tuner. """Run the tuner.
...@@ -49,7 +54,11 @@ class MsgDispatcherBase(Recoverable): ...@@ -49,7 +54,11 @@ class MsgDispatcherBase(Recoverable):
if command is None or command is CommandType.Terminate: if command is None or command is CommandType.Terminate:
break break
if multi_thread_enabled(): if multi_thread_enabled():
self.pool.map_async(self.handle_request, [(command, data)]) result = self.pool.map_async(self.handle_request_thread, [(command, data)])
self.thread_results.append(result)
if any([thread_result.ready() and not thread_result.successful() for thread_result in self.thread_results]):
_logger.debug('Caught thread exception')
break
else: else:
self.handle_request((command, data)) self.handle_request((command, data))
...@@ -59,6 +68,16 @@ class MsgDispatcherBase(Recoverable): ...@@ -59,6 +68,16 @@ class MsgDispatcherBase(Recoverable):
_logger.info('Terminated by NNI manager') _logger.info('Terminated by NNI manager')
def handle_request_thread(self, request):
if multi_thread_enabled():
try:
self.handle_request(request)
except Exception as e:
_logger.exception(str(e))
raise
else:
pass
def handle_request(self, request): def handle_request(self, request):
command, data = request command, data = request
......
...@@ -36,7 +36,7 @@ if not os.path.exists(_outputdir): ...@@ -36,7 +36,7 @@ if not os.path.exists(_outputdir):
os.makedirs(_outputdir) os.makedirs(_outputdir)
_nni_platform = os.environ['NNI_PLATFORM'] _nni_platform = os.environ['NNI_PLATFORM']
if _nni_platform not in ['pai', 'kubeflow']: if _nni_platform not in ['pai', 'kubeflow', 'frameworkcontroller']:
_log_file_path = os.path.join(_outputdir, 'trial.log') _log_file_path = os.path.join(_outputdir, 'trial.log')
init_logger(_log_file_path) init_logger(_log_file_path)
...@@ -77,7 +77,7 @@ def get_next_parameter(): ...@@ -77,7 +77,7 @@ def get_next_parameter():
return params return params
def send_metric(string): def send_metric(string):
if _nni_platform in ['pai', 'kubeflow']: if _nni_platform in ['pai', 'kubeflow', 'frameworkcontroller']:
data = (string).encode('utf8') data = (string).encode('utf8')
assert len(data) < 1000000, 'Metric too long' assert len(data) < 1000000, 'Metric too long'
print('NNISDK_ME%s' % (data)) print('NNISDK_ME%s' % (data))
......
...@@ -48,6 +48,7 @@ class Tuner(Recoverable): ...@@ -48,6 +48,7 @@ class Tuner(Recoverable):
result = [] result = []
for parameter_id in parameter_id_list: for parameter_id in parameter_id_list:
try: try:
_logger.debug("generating param for {}".format(parameter_id))
res = self.generate_parameters(parameter_id) res = self.generate_parameters(parameter_id)
except nni.NoMoreTrialError: except nni.NoMoreTrialError:
return result return result
......
...@@ -17,12 +17,17 @@ ...@@ -17,12 +17,17 @@
.headerCon{ .headerCon{
min-width: 1024px; min-width: 1024px;
} }
.contentBox{
width: 100%;
background: #f2f2f2;
}
.content{ .content{
width: 86%; width: 86%;
min-width: 1024px; min-width: 1024px;
margin: 0 auto; margin: 0 auto;
margin-top: 74px; margin-top: 74px;
margin-bottom: 30px; margin-bottom: 30px;
background: #fff;
} }
...@@ -6,16 +6,18 @@ import SlideBar from './components/SlideBar'; ...@@ -6,16 +6,18 @@ import SlideBar from './components/SlideBar';
class App extends React.Component<{}, {}> { class App extends React.Component<{}, {}> {
render() { render() {
return ( return (
<Row className="nni"> <Row className="nni" style={{minHeight: window.innerHeight}}>
<Row className="header"> <Row className="header">
<Col span={1} /> <Col span={1} />
<Col className="headerCon" span={22}> <Col className="headerCon" span={22}>
<SlideBar /> <SlideBar />
</Col> </Col>
<Col span={1}/> <Col span={1} />
</Row> </Row>
<Row className="content"> <Row className="contentBox">
<Row className="content">
{this.props.children} {this.props.children}
</Row>
</Row> </Row>
</Row> </Row>
); );
......
...@@ -215,6 +215,7 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -215,6 +215,7 @@ class Overview extends React.Component<{}, OverviewState> {
case 'USER_CANCELED': case 'USER_CANCELED':
case 'SYS_CANCELED': case 'SYS_CANCELED':
case 'EARLY_STOPPED':
profile.stopTrial += 1; profile.stopTrial += 1;
break; break;
case 'SUCCEEDED': case 'SUCCEEDED':
...@@ -461,7 +462,10 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -461,7 +462,10 @@ class Overview extends React.Component<{}, OverviewState> {
</Row> </Row>
</Col> </Col>
<Col span={16} id="succeTable"> <Col span={16} id="succeTable">
<SuccessTable tableSource={tableData} /> <SuccessTable
tableSource={tableData}
trainingPlatform={trialProfile.trainingServicePlatform}
/>
</Col> </Col>
</Row> </Row>
</div> </div>
......
...@@ -21,6 +21,7 @@ interface TrialDetailState { ...@@ -21,6 +21,7 @@ interface TrialDetailState {
isHasSearch: boolean; isHasSearch: boolean;
experimentStatus: string; experimentStatus: string;
entriesTable: number; entriesTable: number;
experimentPlatform: string;
} }
class TrialsDetail extends React.Component<{}, TrialDetailState> { class TrialsDetail extends React.Component<{}, TrialDetailState> {
...@@ -43,6 +44,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -43,6 +44,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
experimentStatus: '', experimentStatus: '',
entriesTable: 20, entriesTable: 20,
isHasSearch: false, isHasSearch: false,
experimentPlatform: ''
}; };
} }
// trial accuracy graph // trial accuracy graph
...@@ -370,6 +372,26 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -370,6 +372,26 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
alert('TableList component was not properly initialized.'); alert('TableList component was not properly initialized.');
} }
checkExperimentPlatform = () => {
axios(`${MANAGER_IP}/experiment`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
const trainingPlatform = res.data.params.trainingServicePlatform !== undefined
?
res.data.params.trainingServicePlatform
:
'';
if (this._isMounted) {
this.setState({
experimentPlatform: trainingPlatform
});
}
}
});
}
componentDidMount() { componentDidMount() {
this._isMounted = true; this._isMounted = true;
...@@ -377,6 +399,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -377,6 +399,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
this.drawPointGraph(); this.drawPointGraph();
this.interTableList = window.setInterval(this.drawTableList, 10000); this.interTableList = window.setInterval(this.drawTableList, 10000);
this.interAccuracy = window.setInterval(this.drawPointGraph, 10000); this.interAccuracy = window.setInterval(this.drawPointGraph, 10000);
this.checkExperimentPlatform();
} }
componentWillUnmount() { componentWillUnmount() {
...@@ -386,7 +409,10 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -386,7 +409,10 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
} }
render() { render() {
const { accSource, accNodata, tableListSource, entriesTable, searchResultSource, isHasSearch } = this.state; const { accSource, accNodata, tableListSource,
entriesTable, searchResultSource, isHasSearch,
experimentPlatform
} = this.state;
const titleOfacc = ( const titleOfacc = (
<Title1 text="Default Metric" icon="3.png" /> <Title1 text="Default Metric" icon="3.png" />
); );
...@@ -463,6 +489,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -463,6 +489,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
updateList={this.drawTableList} updateList={this.drawTableList}
searchResult={searchResultSource} searchResult={searchResultSource}
isHasSearch={isHasSearch} isHasSearch={isHasSearch}
platform={experimentPlatform}
ref={(tabList) => this.tableList = tabList} ref={(tabList) => this.tableList = tabList}
/> />
</div> </div>
......
import * as React from 'react';
import { Row, Button } from 'antd';
import { DOWNLOAD_IP } from '../../static/const';
interface PaiTrialChildProps {
logString: string;
id: string;
showLogModal: Function;
isdisLogbtn?: boolean;
}
class PaiTrialChild extends React.Component<PaiTrialChildProps, {}> {
constructor(props: PaiTrialChildProps) {
super(props);
}
render() {
const { logString, id, showLogModal, isdisLogbtn } = this.props;
return (
<div>
{
logString === ''
?
<div />
:
<Row>
<Row>
<a
target="_blank"
href={`${DOWNLOAD_IP}/trial_${id}.log`}
style={{ marginRight: 10 }}
>
trial stdout
</a>
</Row>
<Row>
<Button
disabled={isdisLogbtn}
type="primary"
className="tableButton"
onClick={showLogModal.bind(this, id)}
>
View
</Button>
</Row>
</Row>
}
</div>
);
}
}
export default PaiTrialChild;
import * as React from 'react';
import { Row, Button } from 'antd';
import { DOWNLOAD_IP } from '../../static/const';
import PaiTrialChild from './PaiTrialChild';
interface PaitrialLogProps {
logStr: string;
id: string;
showLogModal: Function;
trialStatus?: string;
isdisLogbutton?: boolean;
}
class PaitrialLog extends React.Component<PaitrialLogProps, {}> {
constructor(props: PaitrialLogProps) {
super(props);
}
render() {
const { logStr, id, showLogModal,
isdisLogbutton
} = this.props;
const isTwopath = logStr.indexOf(',') !== -1
?
true
:
false;
return (
<div>
<div>
{
isTwopath
?
<Row>
<Row>
<a
target="_blank"
href={`${DOWNLOAD_IP}/trial_${id}.log`}
style={{ marginRight: 10 }}
>
trial stdout
</a>
<a target="_blank" href={logStr.split(',')[1]}>hdfsLog</a>
</Row>
<Row>
<Button
disabled={isdisLogbutton}
type="primary"
className="tableButton"
onClick={showLogModal.bind(this, id)}
>
View
</Button>
</Row>
</Row>
:
<PaiTrialChild
logString={logStr}
id={id}
showLogModal={showLogModal}
isdisLogbtn={isdisLogbutton}
/>
}
</div>
</div>
);
}
}
export default PaitrialLog;
import * as React from 'react';
import LogPathChild from './LogPathChild';
interface TrialLogProps {
logStr: string;
id: string;
}
class TrialLog extends React.Component<TrialLogProps, {}> {
constructor(props: TrialLogProps) {
super(props);
}
render() {
const { logStr } = this.props;
return (
<div>
<LogPathChild
eachLogpath={logStr}
logName="LogPath:"
/>
</div>
);
}
}
export default TrialLog;
import * as React from 'react'; import * as React from 'react';
import { import {
Row, Row, Col, Popover, Button, message
Col,
Popover,
Button,
message
} from 'antd'; } from 'antd';
import axios from 'axios'; import axios from 'axios';
import { MANAGER_IP, CONTROLTYPE } from '../../static/const'; import { MANAGER_IP, CONTROLTYPE } from '../../static/const';
...@@ -92,7 +88,8 @@ class Progressed extends React.Component<ProgressProps, ProgressState> { ...@@ -92,7 +88,8 @@ class Progressed extends React.Component<ProgressProps, ProgressState> {
if (error.response.data.error) { if (error.response.data.error) {
message.error(error.response.data.error); message.error(error.response.data.error);
} else { } else {
message.error(`Update ${CONTROLTYPE[1].toLocaleLowerCase()} failed`); message.error(
`Update ${CONTROLTYPE[1].toLocaleLowerCase()} failed`);
} }
} }
}); });
...@@ -179,29 +176,47 @@ class Progressed extends React.Component<ProgressProps, ProgressState> { ...@@ -179,29 +176,47 @@ class Progressed extends React.Component<ProgressProps, ProgressState> {
return ( return (
<Row className="progress" id="barBack"> <Row className="progress" id="barBack">
<Row className="basic lineBasic"> <Row className="basic lineBasic">
<Col span={12}> <p>Status</p>
<p>Status</p> <div className="status">
<div className="status"> <span className={status}>{status}</span>
<span className={status}>{status}</span> {
{ status === 'ERROR'
status === 'ERROR' ?
? <Popover
<Popover placement="rightTop"
placement="rightTop" content={errorContent}
content={errorContent} title="Error"
title="Error" trigger="hover"
trigger="hover" >
> <span className="errorBtn">i</span>
<span className="errorBtn">i</span> </Popover>
</Popover> :
: <span />
<span /> }
} </div>
</div> </Row>
<ProgressBar
who="Duration"
percent={percent}
description={runDuration}
bgclass={status}
maxString={`MaxDuration: ${convertTime(trialProfile.maxDuration)}`}
/>
<ProgressBar
who="TrialNum"
percent={bar2Percent}
description={bar2.toString()}
bgclass={status}
maxString={`MaxTrialNumber: ${trialProfile.MaxTrialNum}`}
/>
<Row className="basic colorOfbasic mess">
<Col span={10}>
<p>best metric</p>
<div>{bestAccuracy.toFixed(6)}</div>
</Col> </Col>
<Col span={12}> <Col span={14}>
{/* modify concurrency */} {/* modify concurrency */}
<p>Concurrency</p> <p>concurrency</p>
<Row className="inputBox"> <Row className="inputBox">
<input <input
type="number" type="number"
...@@ -227,24 +242,6 @@ class Progressed extends React.Component<ProgressProps, ProgressState> { ...@@ -227,24 +242,6 @@ class Progressed extends React.Component<ProgressProps, ProgressState> {
</Row> </Row>
</Col> </Col>
</Row> </Row>
<ProgressBar
who="Duration"
percent={percent}
description={runDuration}
bgclass={status}
maxString={`MaxDuration: ${convertTime(trialProfile.maxDuration)}`}
/>
<ProgressBar
who="TrialNum"
percent={bar2Percent}
description={bar2.toString()}
bgclass={status}
maxString={`MaxTrialNumber: ${trialProfile.MaxTrialNum}`}
/>
<Row className="basic colorOfbasic mess">
<p>best metric</p>
<div>{bestAccuracy}</div>
</Row>
<Row className="mess"> <Row className="mess">
<Col span={8}> <Col span={8}>
<Row className="basic colorOfbasic"> <Row className="basic colorOfbasic">
......
import * as React from 'react'; import * as React from 'react';
import axios from 'axios';
import JSONTree from 'react-json-tree'; import JSONTree from 'react-json-tree';
import { Table } from 'antd'; import { Row, Modal, Input, Table, Tabs } from 'antd';
const TabPane = Tabs.TabPane;
const { TextArea } = Input;
import { DOWNLOAD_IP } from '../../static/const';
import { TableObj } from '../../static/interface'; import { TableObj } from '../../static/interface';
import { convertDuration } from '../../static/function'; import { convertDuration } from '../../static/function';
import PaiTrialLog from '../logPath/PaiTrialLog';
import TrialLog from '../logPath/TrialLog';
import '../../static/style/tableStatus.css'; import '../../static/style/tableStatus.css';
import LogPath from '../logPath/LogPath'; import '../../static/style/tableList.scss';
interface SuccessTableProps { interface SuccessTableProps {
tableSource: Array<TableObj>; tableSource: Array<TableObj>;
trainingPlatform: string;
} }
class SuccessTable extends React.Component<SuccessTableProps, {}> { interface SuccessTableState {
isShowLogModal: boolean;
logContent: string;
}
class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> {
public _isMounted = false;
constructor(props: SuccessTableProps) { constructor(props: SuccessTableProps) {
super(props); super(props);
this.state = {
isShowLogModal: false,
logContent: ''
};
}
showLogModalOverview = (id: string) => {
axios(`${DOWNLOAD_IP}/trial_${id}.log`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
if (this._isMounted) {
this.setState(() => ({
logContent: res.data
}));
}
}
})
.catch(error => {
if (error.response.status === 500) {
if (this._isMounted) {
this.setState(() => ({
logContent: 'failed to get log message'
}));
}
}
});
if (this._isMounted) {
this.setState({
isShowLogModal: true
});
}
}
hideLogModalOverview = () => {
if (this._isMounted) {
this.setState({
isShowLogModal: false,
logContent: '' // close modal, delete data
});
}
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
} }
render() { render() {
const { tableSource } = this.props; const { tableSource, trainingPlatform } = this.props;
const { isShowLogModal, logContent } = this.state;
let bgColor = ''; let bgColor = '';
const columns = [{ const columns = [{
...@@ -114,22 +180,40 @@ class SuccessTable extends React.Component<SuccessTableProps, {}> { ...@@ -114,22 +180,40 @@ class SuccessTable extends React.Component<SuccessTableProps, {}> {
'This trial\'s logPath are not available.'; 'This trial\'s logPath are not available.';
return ( return (
<pre id="description" className="hyperpar"> <pre id="description" className="hyperpar">
{ <Row className="openRowContent">
isHasParameters <Tabs tabPosition="left" className="card">
? <TabPane tab="Parameters" key="1">
<JSONTree {
hideRoot={true} isHasParameters
shouldExpandNode={() => true} // default expandNode ?
getItemString={() => (<span />)} // remove the {} items <JSONTree
data={openRowDataSource} hideRoot={true}
/> shouldExpandNode={() => true} // default expandNode
: getItemString={() => (<span />)} // remove the {} items
<div className="logpath"> data={openRowDataSource}
<span className="logName">Error: </span> />
<span className="error">'This trial's parameters are not available.'</span> :
</div> <div className="logpath">
} <span className="logName">Error: </span>
<LogPath logStr={logPathRow}/> <span className="error">'This trial's parameters are not available.'</span>
</div>
}
</TabPane>
<TabPane tab="Log" key="2">
{
trainingPlatform === 'pai' || trainingPlatform === 'kubeflow'
?
<PaiTrialLog
logStr={logPathRow}
id={record.id}
showLogModal={this.showLogModalOverview}
/>
:
<TrialLog logStr={logPathRow} id={record.id} />
}
</TabPane>
</Tabs>
</Row>
</pre> </pre>
); );
}; };
...@@ -142,6 +226,23 @@ class SuccessTable extends React.Component<SuccessTableProps, {}> { ...@@ -142,6 +226,23 @@ class SuccessTable extends React.Component<SuccessTableProps, {}> {
className="commonTableStyle" className="commonTableStyle"
pagination={false} pagination={false}
/> />
{/* trial log modal */}
<Modal
title="trial log"
visible={isShowLogModal}
onCancel={this.hideLogModalOverview}
footer={null}
destroyOnClose={true}
width="80%"
>
<div id="trialLogContent" style={{ height: window.innerHeight * 0.6 }}>
<TextArea
value={logContent}
disabled={true}
className="logcontent"
/>
</div>
</Modal>
</div> </div>
); );
} }
......
...@@ -2,18 +2,24 @@ import * as React from 'react'; ...@@ -2,18 +2,24 @@ import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import JSONTree from 'react-json-tree'; import JSONTree from 'react-json-tree';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import { Row, Table, Button, Popconfirm, Modal, message, Checkbox } from 'antd'; import {
Row, Input, Table, Tabs, Button, Popconfirm, Modal, message, Checkbox
} from 'antd';
const { TextArea } = Input;
const TabPane = Tabs.TabPane;
const CheckboxGroup = Checkbox.Group; const CheckboxGroup = Checkbox.Group;
import { MANAGER_IP, trialJobStatus, COLUMN, COLUMN_INDEX } from '../../static/const'; import { MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMN, COLUMN_INDEX } from '../../static/const';
import { convertDuration } from '../../static/function'; import { convertDuration } from '../../static/function';
import { TableObjFianl, TrialJob } from '../../static/interface'; import { TableObjFianl, TrialJob } from '../../static/interface';
import LogPath from '../logPath/LogPath'; import PaiTrialLog from '../logPath/PaiTrialLog';
import TrialLog from '../logPath/TrialLog';
import '../../static/style/search.scss'; import '../../static/style/search.scss';
require('../../static/style/tableStatus.css'); require('../../static/style/tableStatus.css');
require('../../static/style/logPath.scss'); require('../../static/style/logPath.scss');
require('../../static/style/search.scss'); require('../../static/style/search.scss');
require('../../static/style/table.scss'); require('../../static/style/table.scss');
require('../../static/style/button.scss'); require('../../static/style/button.scss');
require('../../static/style/tableList.scss');
const echarts = require('echarts/lib/echarts'); const echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/line'); require('echarts/lib/chart/line');
require('echarts/lib/component/tooltip'); require('echarts/lib/component/tooltip');
...@@ -28,6 +34,7 @@ interface TableListProps { ...@@ -28,6 +34,7 @@ interface TableListProps {
searchResult: Array<TableObjFianl>; searchResult: Array<TableObjFianl>;
updateList: Function; updateList: Function;
isHasSearch: boolean; isHasSearch: boolean;
platform: string;
} }
interface TableListState { interface TableListState {
...@@ -36,6 +43,8 @@ interface TableListState { ...@@ -36,6 +43,8 @@ interface TableListState {
isObjFinal: boolean; isObjFinal: boolean;
isShowColumn: boolean; isShowColumn: boolean;
columnSelected: Array<string>; // user select columnKeys columnSelected: Array<string>; // user select columnKeys
logModal: boolean;
logMessage: string;
} }
interface ColumnIndex { interface ColumnIndex {
...@@ -46,6 +55,9 @@ interface ColumnIndex { ...@@ -46,6 +55,9 @@ interface ColumnIndex {
class TableList extends React.Component<TableListProps, TableListState> { class TableList extends React.Component<TableListProps, TableListState> {
public _isMounted = false; public _isMounted = false;
public intervalTrialLog = 10;
public _trialId: string;
constructor(props: TableListProps) { constructor(props: TableListProps) {
super(props); super(props);
...@@ -54,7 +66,9 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -54,7 +66,9 @@ class TableList extends React.Component<TableListProps, TableListState> {
modalVisible: false, modalVisible: false,
isObjFinal: false, isObjFinal: false,
isShowColumn: false, isShowColumn: false,
logModal: false,
columnSelected: COLUMN, columnSelected: COLUMN,
logMessage: ''
}; };
} }
...@@ -92,6 +106,51 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -92,6 +106,51 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
} }
updateTrialLogMessage = (id: string) => {
this._trialId = id;
axios(`${DOWNLOAD_IP}/trial_${this._trialId}.log`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
if (this._isMounted) {
this.setState(() => ({
logMessage: res.data
}));
}
}
})
.catch(error => {
if (error.response.status === 500) {
if (this._isMounted) {
this.setState(() => ({
logMessage: 'failed to get log message'
}));
}
}
});
}
showLogModal = (id: string) => {
this.updateTrialLogMessage(id);
this.intervalTrialLog = window.setInterval(this.updateTrialLogMessage.bind(this, this._trialId), 10000);
if (this._isMounted) {
this.setState({
logModal: true
});
}
}
hideLogModal = () => {
window.clearInterval(this.intervalTrialLog);
if (this._isMounted) {
this.setState({
logModal: false,
logMessage: ''
});
}
}
hideShowColumnModal = () => { hideShowColumnModal = () => {
if (this._isMounted) { if (this._isMounted) {
this.setState({ this.setState({
...@@ -235,11 +294,14 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -235,11 +294,14 @@ class TableList extends React.Component<TableListProps, TableListState> {
render() { render() {
const { entries, tableSource, searchResult, isHasSearch } = this.props; const { entries, tableSource, searchResult, isHasSearch, platform } = this.props;
const { intermediateOption, modalVisible, isShowColumn, columnSelected, const { intermediateOption, modalVisible, isShowColumn, columnSelected,
logMessage, logModal
} = this.state; } = this.state;
let showTitle = COLUMN; let showTitle = COLUMN;
let bgColor = '';
const trialJob: Array<TrialJob> = [];
const showColumn: Array<object> = [];
if (tableSource.length >= 1) { if (tableSource.length >= 1) {
const temp = tableSource[0].acc; const temp = tableSource[0].acc;
if (temp !== undefined && typeof temp === 'object') { if (temp !== undefined && typeof temp === 'object') {
...@@ -256,16 +318,12 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -256,16 +318,12 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
} }
} }
let bgColor = '';
const trialJob: Array<TrialJob> = [];
trialJobStatus.map(item => { trialJobStatus.map(item => {
trialJob.push({ trialJob.push({
text: item, text: item,
value: item value: item
}); });
}); });
const showColumn: Array<object> = [];
Object.keys(columnSelected).map(key => { Object.keys(columnSelected).map(key => {
const item = columnSelected[key]; const item = columnSelected[key];
switch (item) { switch (item) {
...@@ -473,24 +531,49 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -473,24 +531,49 @@ class TableList extends React.Component<TableListProps, TableListState> {
record.description.logPath record.description.logPath
: :
'This trial\'s logPath are not available.'; 'This trial\'s logPath are not available.';
const isdisLogbutton = record.status === 'WAITING'
?
true
:
false;
return ( return (
<pre id="allList" className="hyperpar"> <pre id="allList" className="hyperpar">
{ <Row className="openRowContent">
isHasParameters <Tabs tabPosition="left" className="card">
? <TabPane tab="Parameters" key="1">
< JSONTree {
hideRoot={true} isHasParameters
shouldExpandNode={() => true} // default expandNode ?
getItemString={() => (<span />)} // remove the {} items <JSONTree
data={parametersRow} hideRoot={true}
/> shouldExpandNode={() => true} // default expandNode
: getItemString={() => (<span />)} // remove the {} items
<div className="logpath"> data={parametersRow}
<span className="logName">Error: </span> />
<span className="error">'This trial's parameters are not available.'</span> :
</div> <div className="logpath">
} <span className="logName">Error: </span>
<LogPath logStr={logPathRow} /> <span className="error">'This trial's parameters are not available.'</span>
</div>
}
</TabPane>
<TabPane tab="Log" key="2">
{
platform === 'pai' || platform === 'kubeflow'
?
<PaiTrialLog
logStr={logPathRow}
id={record.id}
showLogModal={this.showLogModal}
trialStatus={record.status}
isdisLogbutton={isdisLogbutton}
/>
:
<TrialLog logStr={logPathRow} id={record.id} />
}
</TabPane>
</Tabs>
</Row>
</pre> </pre>
); );
}; };
...@@ -523,6 +606,24 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -523,6 +606,24 @@ class TableList extends React.Component<TableListProps, TableListState> {
theme="my_theme" theme="my_theme"
/> />
</Modal> </Modal>
{/* trial log modal */}
<Modal
title="trial log"
visible={logModal}
onCancel={this.hideLogModal}
footer={null}
destroyOnClose={true}
width="80%"
>
<div id="trialLogContent" style={{ height: window.innerHeight * 0.6 }}>
<TextArea
value={logMessage}
disabled={true}
className="logcontent"
/>
</div>
</Modal>
</div> </div>
{/* Add Column Modal */} {/* Add Column Modal */}
<Modal <Modal
...@@ -540,9 +641,10 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -540,9 +641,10 @@ class TableList extends React.Component<TableListProps, TableListState> {
className="titleColumn" className="titleColumn"
/> />
</Modal> </Modal>
</Row> </Row>
); );
} }
} }
export default TableList; export default TableList;
\ No newline at end of file
#barBack{ #barBack{
/* status: 'INITIALIZED' | 'RUNNING' | 'ERROR' | 'STOPPING' | 'STOPPED' | 'DONE' */ /* status: 'INITIALIZED' | 'RUNNING' | 'ERROR' | 'STOPPING' | 'STOPPED' | 'DONE' */
.RUNNING, .STOPPING, .INITIALIZED{ /* status: 'TUNER_NO_MORE_TRIAL' | 'NO_MORE_TRIAL' */
.RUNNING, .STOPPING, .INITIALIZED, .NO_MORE_TRIAL, .TUNER_NO_MORE_TRIAL{
/* specific status color */ /* specific status color */
color: #0071bc; color: #0071bc;
.ant-progress-bg { .ant-progress-bg {
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
*/ */
.inputBox{ .inputBox{
height: 26px; height: 26px;
margin-top: -3px;
.concurrencyInput{ .concurrencyInput{
width: 25%; width: 25%;
height: 26px; height: 26px;
......
$color: #0071bc;
.openRowContent{
.ant-tabs-vertical.ant-tabs-left .ant-tabs-bar .ant-tabs-tab{
text-align: left !important;
}
}
.openRowContent{
.ant-tabs-nav .ant-tabs-tab:hover{
color: $color;
}
}
.openRowContent{
margin-top: 15px;
.card{
.ant-tabs-vertical .ant-tabs-bar .ant-tabs-tab{
margin: 0;
padding: 8px 12px;
}
.ant-tabs-nav .ant-tabs-tab-active{
color: $color;
font-weight: 500;
background: #dedede;
border-left: 3px solid ;
}
.ant-tabs-tabpane{
background: #f2f2f2;
padding: 10px 20px;
}
}
}
.trialLog{
white-space: normal;
color: #212121;
}
#trialLogContent{
.ant-input-disabled{
color: #212121;
background-color: #fff;
cursor: pointer;
border: none;
}
.logcontent{
height: 100%;
}
}
authorName: default
experimentName: example_weight_sharing
trialConcurrency: 3
maxExecDuration: 1h
maxTrialNum: 10
#choice: local, remote, pai
trainingServicePlatform: remote
#choice: true, false
useAnnotation: false
multiThread: true
tuner:
codeDir: .
classFileName: simple_tuner.py
className: SimpleTuner
trial:
command: python3 main.py
codeDir: .
gpuNum: 0
machineList:
- ip: 10.10.10.10
username: bob
passwd: bob123
- ip: 10.10.10.11
username: bob
passwd: bob123
"""
Test code for weight sharing
need NFS setup and mounted as `/mnt/nfs/nni`
"""
import hashlib
import os
import random
import time
import nni
def generate_rand_file(fl_name):
"""
generate random file and write to `fl_name`
"""
fl_size = random.randint(1024, 102400)
fl_dir = os.path.split(fl_name)[0]
if not os.path.exists(fl_dir):
os.makedirs(fl_dir)
with open(fl_name, 'wb') as fout:
fout.write(os.urandom(fl_size))
def check_sum(fl_name, tid=None):
"""
compute checksum for generated file of `fl_name`
"""
hasher = hashlib.md5()
with open(fl_name, 'rb') as fin:
for chunk in iter(lambda: fin.read(4096), b""):
hasher.update(chunk)
ret = hasher.hexdigest()
if tid is not None:
ret = ret + str(tid)
return ret
if __name__ == '__main__':
nfs_path = '/mnt/nfs/nni/test'
params = nni.get_next_parameter()
print(params)
if params['id'] == 0:
model_file = os.path.join(nfs_path, str(params['id']), 'model.dat')
generate_rand_file(model_file)
time.sleep(10)
nni.report_final_result({
'checksum': check_sum(model_file, tid=params['id']),
'path': model_file
})
else:
model_file = params['prev_path']
time.sleep(10)
nni.report_final_result({
'checksum': check_sum(model_file, tid=params['prev_id'])
})
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