diff --git a/cmd/ui/v1alpha3/main.go b/cmd/ui/v1alpha3/main.go index 1be8ff71856..50dcfb6a39a 100644 --- a/cmd/ui/v1alpha3/main.go +++ b/cmd/ui/v1alpha3/main.go @@ -29,7 +29,7 @@ func main() { http.Handle("/katib/", http.StripPrefix("/katib/", frontend)) http.HandleFunc("/katib/fetch_hp_jobs/", kuh.FetchAllHPJobs) - http.HandleFunc("/katib/fetch_nas_jobs/", kuh.FetchNASJobs) + http.HandleFunc("/katib/fetch_nas_jobs/", kuh.FetchAllNASJobs) http.HandleFunc("/katib/submit_yaml/", kuh.SubmitYamlJob) http.HandleFunc("/katib/submit_hp_job/", kuh.SubmitParamsJob) http.HandleFunc("/katib/submit_nas_job/", kuh.SubmitParamsJob) diff --git a/pkg/ui/v1alpha3/backend.go b/pkg/ui/v1alpha3/backend.go index d9b0aa75779..4a8f3ee5270 100644 --- a/pkg/ui/v1alpha3/backend.go +++ b/pkg/ui/v1alpha3/backend.go @@ -1,12 +1,9 @@ package v1alpha3 import ( - "context" "encoding/json" "log" "net/http" - "strings" - "time" "github.com/ghodss/yaml" "google.golang.org/grpc" @@ -14,7 +11,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" experimentv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1alpha3" - trialsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1alpha3" api_pb_v1alpha3 "github.com/kubeflow/katib/pkg/apis/manager/v1alpha3" common_v1alpha3 "github.com/kubeflow/katib/pkg/common/v1alpha3" "github.com/kubeflow/katib/pkg/controller.v1alpha3/consts" @@ -42,41 +38,6 @@ func (k *KatibUIHandler) connectManager() (*grpc.ClientConn, api_pb_v1alpha3.Man return conn, c } -func (k *KatibUIHandler) FetchHPJobs(w http.ResponseWriter, r *http.Request) { - //enableCors(&w) - jobs, err := k.getExperimentList(consts.DefaultKatibNamespace, JobTypeHP) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - response, err := json.Marshal(jobs) - if err != nil { - log.Printf("Marshal HP jobs failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Write(response) - -} - -// FetchAllHPJobs gets experiments in all namespaces. -func (k *KatibUIHandler) FetchAllHPJobs(w http.ResponseWriter, r *http.Request) { - // Use "" to get experiments in all namespaces. - jobs, err := k.getExperimentList("", JobTypeHP) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - response, err := json.Marshal(jobs) - if err != nil { - log.Printf("Marshal HP jobs failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Write(response) -} - func (k *KatibUIHandler) SubmitYamlJob(w http.ResponseWriter, r *http.Request) { //enableCors(&w) var data map[string]interface{} @@ -155,128 +116,6 @@ func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request } } -func (k *KatibUIHandler) FetchHPJobInfo(w http.ResponseWriter, r *http.Request) { - //enableCors(&w) - experimentName := r.URL.Query()["experimentName"][0] - namespace := r.URL.Query()["namespace"][0] - - conn, c := k.connectManager() - defer conn.Close() - - resultText := "trialName" - experiment, err := k.katibClient.GetExperiment(experimentName, namespace) - if err != nil { - log.Printf("GetExperiment from HP job failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Printf("Got Experiment") - metricsList := map[string]int{} - metricsName := experiment.Spec.Objective.ObjectiveMetricName - resultText += "," + metricsName - metricsList[metricsName] = 0 - for i, m := range experiment.Spec.Objective.AdditionalMetricNames { - resultText += "," + m - metricsList[m] = i + 1 - } - log.Printf("Got metrics names") - paramList := map[string]int{} - for i, p := range experiment.Spec.Parameters { - resultText += "," + p.Name - paramList[p.Name] = i + len(metricsList) - } - log.Printf("Got Parameters names") - - trialList, err := k.katibClient.GetTrialList(experimentName) - if err != nil { - log.Printf("GetTrialList from HP job failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Printf("Got Trial List") - - for _, t := range trialList.Items { - succeeded := false - for _, condition := range t.Status.Conditions { - if condition.Type == trialsv1alpha3.TrialSucceeded { - succeeded = true - } - } - if succeeded { - obsLogResp, err := c.GetObservationLog( - context.Background(), - &api_pb_v1alpha3.GetObservationLogRequest{ - TrialName: t.Name, - StartTime: "", - EndTime: "", - }, - ) - if err != nil { - log.Printf("GetObservationLog from HP job failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - trialResText := make([]string, len(metricsList)+len(paramList)) - for _, m := range obsLogResp.ObservationLog.MetricLogs { - trialResText[metricsList[m.Metric.Name]] = m.Metric.Value - - } - for _, trialParam := range t.Spec.ParameterAssignments { - trialResText[paramList[trialParam.Name]] = trialParam.Value - } - resultText += "\n" + t.Name + "," + strings.Join(trialResText, ",") - } - } - log.Printf("Logs parsed, results:\n %v", resultText) - response, err := json.Marshal(resultText) - if err != nil { - log.Printf("Marshal result text for HP job failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Write(response) -} - -func (k *KatibUIHandler) FetchHPJobTrialInfo(w http.ResponseWriter, r *http.Request) { - //enableCors(&w) - trialName := r.URL.Query()["trialName"][0] - conn, c := k.connectManager() - defer conn.Close() - - resultText := "metricName,time,value\n" - obsLogResp, err := c.GetObservationLog( - context.Background(), - &api_pb_v1alpha3.GetObservationLogRequest{ - TrialName: trialName, - StartTime: "", - EndTime: "", - }, - ) - if err != nil { - log.Printf("GetObservationLog failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - prevTime := "" - for _, m := range obsLogResp.ObservationLog.MetricLogs { - parsedTime, _ := time.Parse(time.RFC3339Nano, m.TimeStamp) - formatTime := parsedTime.Format("2006-01-02T15:4:5") - if formatTime != prevTime { - resultText += m.Metric.Name + "," + formatTime + "," + m.Metric.Value + "\n" - prevTime = formatTime - } - } - - response, err := json.Marshal(resultText) - if err != nil { - log.Printf("Marshal result text in Trial info failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Write(response) -} - // FetchTrialTemplates gets the trial templates for the given namespace. func (k *KatibUIHandler) FetchTrialTemplates(w http.ResponseWriter, r *http.Request) { //enableCors(&w) diff --git a/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js b/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js index 7542ed36466..666984c5199 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js @@ -171,6 +171,13 @@ export const changeTrial = (trial) => ({ trial, }) +export const CHANGE_TRIAL_NAMESPACE_NAS = "CHANGE_TRIAL_NAMESPACE_HP"; + +export const changeTrialNamespace = (namespace) => ({ + type: CHANGE_TRIAL_NAMESPACE_NAS, + namespace, +}) + export const SUBMIT_NAS_JOB_REQUEST = "SUBMIT_NAS_JOB_REQUEST"; export const SUBMIT_NAS_JOB_SUCCESS = "SUBMIT_NAS_JOB_SUCCESS"; export const SUBMIT_NAS_JOB_FAILURE = "SUBMIT_NAS_JOB_FAILURE"; diff --git a/pkg/ui/v1alpha3/frontend/src/actions/nasMonitorActions.js b/pkg/ui/v1alpha3/frontend/src/actions/nasMonitorActions.js index 4796d91ec5e..f38855b5bcc 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/nasMonitorActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/nasMonitorActions.js @@ -24,7 +24,8 @@ export const FETCH_NAS_JOB_INFO_REQUEST = "FETCH_NAS_JOB_INFO_REQUEST"; export const FETCH_NAS_JOB_INFO_SUCCESS = "FETCH_NAS_JOB_INFO_SUCCESS"; export const FETCH_NAS_JOB_INFO_FAILURE = "FETCH_NAS_JOB_INFO_FAILURE"; -export const fetchNASJobInfo = (experimentName) => ({ +export const fetchNASJobInfo = (experimentName, namespace) => ({ type: FETCH_NAS_JOB_INFO_REQUEST, - experimentName + experimentName, + namespace }) diff --git a/pkg/ui/v1alpha3/frontend/src/components/App.jsx b/pkg/ui/v1alpha3/frontend/src/components/App.jsx index e2c727f9575..0cf717147dc 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/App.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/App.jsx @@ -35,7 +35,7 @@ const App = (props) => { - + diff --git a/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx b/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx index 170cc87dd6d..798ff52c61a 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx @@ -144,7 +144,7 @@ const NASParameters = (props) => { goTemplate: { templateSpec: { configMapName: "trial-template", - configMapNamespace: data.metadata.namespace, + configMapNamespace: props.trialNamespace, templatePath: props.trial, } } @@ -191,7 +191,8 @@ const mapStateToProps = (state) => ({ inputSize: state[module].inputSize, outputSize: state[module].outputSize, operations: state[module].operations, - trial: state[module].trial + trial: state[module].trial, + trialNamespace: state[module].trialNamespace, }) //TODO: Added validation and remove it diff --git a/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/Params/Trial.jsx b/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/Params/Trial.jsx index cabe3abe306..6bea3f2731c 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/Params/Trial.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/Params/Trial.jsx @@ -9,9 +9,10 @@ import InputLabel from '@material-ui/core/InputLabel'; import MenuItem from '@material-ui/core/MenuItem'; import FormControl from '@material-ui/core/FormControl'; import Select from '@material-ui/core/Select'; +import TextField from '@material-ui/core/TextField'; import { connect } from 'react-redux'; -import { changeTrial } from '../../../../actions/nasCreateActions'; +import { changeTrial, changeTrialNamespace } from '../../../../actions/nasCreateActions'; import { fetchTrialTemplates } from '../../../../actions/templateActions'; const module = "nasCreate"; @@ -41,8 +42,9 @@ const styles = theme => ({ class TrialSpecParam extends React.Component { - componentDidMount() { - this.props.fetchTrialTemplates(); + onTrialNamespaceChange = (event) => { + this.props.fetchTrialTemplates(event.target.value); + this.props.changeTrialNamespace(event.target.value); } onTrialChange = (event) => { @@ -54,38 +56,59 @@ class TrialSpecParam extends React.Component { const { classes } = this.props return ( -
- - - - - - - {"TrialSpec"} - +
+
+ + + + + + + {"Namespace"} + + + + + - - - - Trial Spec - - + } + className={classes.select} > {names.map((spec, i) => { return ( - {spec} - ) + {spec} + ) })} - - + + + - +
) } @@ -96,7 +119,8 @@ const mapStateToProps = state => { return { trial: state[module].trial, templates: state[templateModule].trialTemplates, + trialNamespace: state[module].trialNamespace, } } -export default connect(mapStateToProps, { changeTrial, fetchTrialTemplates })(withStyles(styles)(TrialSpecParam)); +export default connect(mapStateToProps, { changeTrialNamespace, changeTrial, fetchTrialTemplates })(withStyles(styles)(TrialSpecParam)); diff --git a/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobInfo.jsx b/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobInfo.jsx index 229cdeae55e..48e8d7ee743 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobInfo.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobInfo.jsx @@ -39,10 +39,11 @@ const styles = theme => ({ class NASJobInfo extends React.Component { componentDidMount() { - this.props.fetchNASJobInfo(this.props.match.params.name); + this.props.fetchNASJobInfo( + this.props.match.params.name, this.props.match.params.namespace); } - render () { + render() { const { classes } = this.props; return (
@@ -51,29 +52,32 @@ class NASJobInfo extends React.Component { Back - {this.props.loading ? - - : -
- - Experiment Name: {this.props.match.params.name} - -
- {this.props.steps.map((step, i) => { - return ( - - }> - {step.name} - - - - - - ) - })} -
+ {this.props.loading ? + + : +
+ + Experiment Name: {this.props.match.params.name} + + + Experiment Namespace: {this.props.match.params.namespace} + +
+ {this.props.steps.map((step, i) => { + return ( + + }> + {step.name} + + + + + + ) + })} +
} - +
) } diff --git a/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobList.jsx b/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobList.jsx index 44872b364d0..8307f10d2a6 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobList.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/NAS/Monitor/NASJobList.jsx @@ -43,8 +43,8 @@ const NASJobList = (props) => { const { classes } = props; - const onDeleteExperiment = (experimentName) => (event) => { - props.openDeleteExperimentDialog(experimentName); + const onDeleteExperiment = (name, namespace) => (event) => { + props.openDeleteExperimentDialog(name, namespace); } return ( @@ -64,13 +64,13 @@ const NASJobList = (props) => { icon = () } return ( - + {icon} - + - + diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js b/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js index 978d1b74891..2aa6ef4ce1f 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js @@ -550,6 +550,11 @@ const nasCreateReducer = (state = initialState, action) => { ...state, trial: action.trial, } + case actions.CHANGE_TRIAL_NAMESPACE_NAS: + return { + ...state, + trialNamespace: action.namespace, + } case actions.CLOSE_SNACKBAR: return { ...state, diff --git a/pkg/ui/v1alpha3/frontend/src/sagas/index.js b/pkg/ui/v1alpha3/frontend/src/sagas/index.js index 8b87a797213..599ba138834 100644 --- a/pkg/ui/v1alpha3/frontend/src/sagas/index.js +++ b/pkg/ui/v1alpha3/frontend/src/sagas/index.js @@ -367,7 +367,8 @@ export const fetchNASJobInfo = function* () { try { const result = yield call( goFetchNASJobInfo, - action.experimentName + action.experimentName, + action.namespace, ) if (result.status === 200) { let data = Object.assign(result.data, {}) @@ -395,11 +396,11 @@ export const fetchNASJobInfo = function* () { } } -const goFetchNASJobInfo = function* (experimentName) { +const goFetchNASJobInfo = function* (experimentName, namespace) { try { const result = yield call( axios.get, - `/katib/fetch_nas_job_info/?experimentName=${experimentName}`, + `/katib/fetch_nas_job_info/?experimentName=${experimentName}&namespace=${namespace}`, ) return result } catch (err) { diff --git a/pkg/ui/v1alpha3/hp.go b/pkg/ui/v1alpha3/hp.go new file mode 100644 index 00000000000..381d33f3230 --- /dev/null +++ b/pkg/ui/v1alpha3/hp.go @@ -0,0 +1,152 @@ +package v1alpha3 + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strings" + "time" + + trialsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1alpha3" + api_pb_v1alpha3 "github.com/kubeflow/katib/pkg/apis/manager/v1alpha3" +) + +// FetchAllHPJobs gets experiments in all namespaces. +func (k *KatibUIHandler) FetchAllHPJobs(w http.ResponseWriter, r *http.Request) { + // Use "" to get experiments in all namespaces. + jobs, err := k.getExperimentList("", JobTypeHP) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + response, err := json.Marshal(jobs) + if err != nil { + log.Printf("Marshal HP jobs failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +func (k *KatibUIHandler) FetchHPJobInfo(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + experimentName := r.URL.Query()["experimentName"][0] + namespace := r.URL.Query()["namespace"][0] + + conn, c := k.connectManager() + defer conn.Close() + + resultText := "trialName" + experiment, err := k.katibClient.GetExperiment(experimentName, namespace) + if err != nil { + log.Printf("GetExperiment from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Printf("Got Experiment") + metricsList := map[string]int{} + metricsName := experiment.Spec.Objective.ObjectiveMetricName + resultText += "," + metricsName + metricsList[metricsName] = 0 + for i, m := range experiment.Spec.Objective.AdditionalMetricNames { + resultText += "," + m + metricsList[m] = i + 1 + } + log.Printf("Got metrics names") + paramList := map[string]int{} + for i, p := range experiment.Spec.Parameters { + resultText += "," + p.Name + paramList[p.Name] = i + len(metricsList) + } + log.Printf("Got Parameters names") + + trialList, err := k.katibClient.GetTrialList(experimentName) + if err != nil { + log.Printf("GetTrialList from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Printf("Got Trial List") + + for _, t := range trialList.Items { + succeeded := false + for _, condition := range t.Status.Conditions { + if condition.Type == trialsv1alpha3.TrialSucceeded { + succeeded = true + } + } + if succeeded { + obsLogResp, err := c.GetObservationLog( + context.Background(), + &api_pb_v1alpha3.GetObservationLogRequest{ + TrialName: t.Name, + StartTime: "", + EndTime: "", + }, + ) + if err != nil { + log.Printf("GetObservationLog from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + trialResText := make([]string, len(metricsList)+len(paramList)) + for _, m := range obsLogResp.ObservationLog.MetricLogs { + trialResText[metricsList[m.Metric.Name]] = m.Metric.Value + + } + for _, trialParam := range t.Spec.ParameterAssignments { + trialResText[paramList[trialParam.Name]] = trialParam.Value + } + resultText += "\n" + t.Name + "," + strings.Join(trialResText, ",") + } + } + log.Printf("Logs parsed, results:\n %v", resultText) + response, err := json.Marshal(resultText) + if err != nil { + log.Printf("Marshal result text for HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +func (k *KatibUIHandler) FetchHPJobTrialInfo(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + trialName := r.URL.Query()["trialName"][0] + conn, c := k.connectManager() + defer conn.Close() + + resultText := "metricName,time,value\n" + obsLogResp, err := c.GetObservationLog( + context.Background(), + &api_pb_v1alpha3.GetObservationLogRequest{ + TrialName: trialName, + StartTime: "", + EndTime: "", + }, + ) + if err != nil { + log.Printf("GetObservationLog failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + prevTime := "" + for _, m := range obsLogResp.ObservationLog.MetricLogs { + parsedTime, _ := time.Parse(time.RFC3339Nano, m.TimeStamp) + formatTime := parsedTime.Format("2006-01-02T15:4:5") + if formatTime != prevTime { + resultText += m.Metric.Name + "," + formatTime + "," + m.Metric.Value + "\n" + prevTime = formatTime + } + } + + response, err := json.Marshal(resultText) + if err != nil { + log.Printf("Marshal result text in Trial info failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} diff --git a/pkg/ui/v1alpha3/nas.go b/pkg/ui/v1alpha3/nas.go index 02bb1c7fde1..6ff6b52843a 100644 --- a/pkg/ui/v1alpha3/nas.go +++ b/pkg/ui/v1alpha3/nas.go @@ -9,12 +9,12 @@ import ( trialsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1alpha3" api_pb_v1alpha3 "github.com/kubeflow/katib/pkg/apis/manager/v1alpha3" - "github.com/kubeflow/katib/pkg/controller.v1alpha3/consts" ) -func (k *KatibUIHandler) FetchNASJobs(w http.ResponseWriter, r *http.Request) { +func (k *KatibUIHandler) FetchAllNASJobs(w http.ResponseWriter, r *http.Request) { //enableCors(&w) - jobs, err := k.getExperimentList(consts.DefaultKatibNamespace, JobTypeNAS) + // Use "" to get experiments in all namespaces. + jobs, err := k.getExperimentList("", JobTypeNAS) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -32,6 +32,7 @@ func (k *KatibUIHandler) FetchNASJobs(w http.ResponseWriter, r *http.Request) { func (k *KatibUIHandler) FetchNASJobInfo(w http.ResponseWriter, r *http.Request) { //enableCors(&w) experimentName := r.URL.Query()["experimentName"][0] + namespace := r.URL.Query()["namespace"][0] responseRaw := make([]NNView, 0) var architecture string @@ -41,7 +42,7 @@ func (k *KatibUIHandler) FetchNASJobInfo(w http.ResponseWriter, r *http.Request) defer conn.Close() - trials, err := k.katibClient.GetTrialList(experimentName) + trials, err := k.katibClient.GetTrialList(experimentName, namespace) if err != nil { log.Printf("GetTrialList from NAS job failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError)