diff --git a/cmd/ui/v1alpha3/main.go b/cmd/ui/v1alpha3/main.go index d299c6f40f6..1be8ff71856 100644 --- a/cmd/ui/v1alpha3/main.go +++ b/cmd/ui/v1alpha3/main.go @@ -6,6 +6,8 @@ import ( "log" "net/http" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + ui "github.com/kubeflow/katib/pkg/ui/v1alpha3" ) @@ -26,7 +28,7 @@ func main() { frontend := http.FileServer(http.Dir(*buildDir)) http.Handle("/katib/", http.StripPrefix("/katib/", frontend)) - http.HandleFunc("/katib/fetch_hp_jobs/", kuh.FetchHPJobs) + http.HandleFunc("/katib/fetch_hp_jobs/", kuh.FetchAllHPJobs) http.HandleFunc("/katib/fetch_nas_jobs/", kuh.FetchNASJobs) http.HandleFunc("/katib/submit_yaml/", kuh.SubmitYamlJob) http.HandleFunc("/katib/submit_hp_job/", kuh.SubmitParamsJob) diff --git a/pkg/ui/v1alpha3/backend.go b/pkg/ui/v1alpha3/backend.go index 63375e648fe..d9b0aa75779 100644 --- a/pkg/ui/v1alpha3/backend.go +++ b/pkg/ui/v1alpha3/backend.go @@ -1,18 +1,16 @@ -package ui +package v1alpha3 import ( "context" "encoding/json" "log" "net/http" - "strconv" "strings" "time" "github.com/ghodss/yaml" "google.golang.org/grpc" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client" experimentv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1alpha3" @@ -46,29 +44,11 @@ func (k *KatibUIHandler) connectManager() (*grpc.ClientConn, api_pb_v1alpha3.Man func (k *KatibUIHandler) FetchHPJobs(w http.ResponseWriter, r *http.Request) { //enableCors(&w) - - jobs := make([]JobView, 0) - - el, err := k.katibClient.GetExperimentList() + jobs, err := k.getExperimentList(consts.DefaultKatibNamespace, JobTypeHP) if err != nil { - log.Printf("GetExperimentList for HP failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - for _, experiment := range el.Items { - if experiment.Spec.Parameters != nil { - experimentLastCondition, err := experiment.GetLastConditionType() - if err != nil { - log.Printf("GetLastConditionType for HP failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - jobs = append(jobs, JobView{ - Name: experiment.Name, - Status: string(experimentLastCondition), - }) - } - } response, err := json.Marshal(jobs) if err != nil { @@ -80,40 +60,21 @@ func (k *KatibUIHandler) FetchHPJobs(w http.ResponseWriter, r *http.Request) { } -func (k *KatibUIHandler) FetchNASJobs(w http.ResponseWriter, r *http.Request) { - //enableCors(&w) - - jobs := make([]JobView, 0) - - el, err := k.katibClient.GetExperimentList() +// 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 { - log.Printf("GetExperimentList for NAS failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - for _, experiment := range el.Items { - if experiment.Spec.NasConfig != nil { - experimentLastCondition, err := experiment.GetLastConditionType() - if err != nil { - log.Printf("GetLastConditionType for HP failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - jobs = append(jobs, JobView{ - Name: experiment.Name, - Status: string(experimentLastCondition), - }) - } - } - response, err := json.Marshal(jobs) if err != nil { - log.Printf("Marshal NAS jobs failed: %v", err) + 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) { @@ -178,7 +139,9 @@ func (k *KatibUIHandler) SubmitParamsJob(w http.ResponseWriter, r *http.Request) func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request) { experimentName := r.URL.Query()["experimentName"][0] - experiment, err := k.katibClient.GetExperiment(experimentName) + namespace := r.URL.Query()["namespace"][0] + + experiment, err := k.katibClient.GetExperiment(experimentName, namespace) if err != nil { log.Printf("GetExperiment failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -195,12 +158,13 @@ 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) + 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) @@ -272,7 +236,6 @@ func (k *KatibUIHandler) FetchHPJobInfo(w http.ResponseWriter, r *http.Request) return } w.Write(response) - } func (k *KatibUIHandler) FetchHPJobTrialInfo(w http.ResponseWriter, r *http.Request) { @@ -314,82 +277,6 @@ func (k *KatibUIHandler) FetchHPJobTrialInfo(w http.ResponseWriter, r *http.Requ w.Write(response) } -func (k *KatibUIHandler) FetchNASJobInfo(w http.ResponseWriter, r *http.Request) { - //enableCors(&w) - experimentName := r.URL.Query()["experimentName"][0] - - responseRaw := make([]NNView, 0) - var architecture string - var decoder string - - conn, c := k.connectManager() - - defer conn.Close() - - trials, err := k.katibClient.GetTrialList(experimentName) - if err != nil { - log.Printf("GetTrialList from NAS job failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Printf("Got Trial List") - - for i, t := range trials.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 NAS job failed: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - metricsName := make([]string, 0) - metricsValue := make([]string, 0) - for _, m := range obsLogResp.ObservationLog.MetricLogs { - metricsName = append(metricsName, m.Metric.Name) - metricsValue = append(metricsValue, m.Metric.Value) - - } - for _, trialParam := range t.Spec.ParameterAssignments { - if trialParam.Name == "architecture" { - architecture = trialParam.Value - } - if trialParam.Name == "nn_config" { - decoder = trialParam.Value - } - } - responseRaw = append(responseRaw, NNView{ - Name: "Generation " + strconv.Itoa(i), - TrialName: t.Name, - Architecture: generateNNImage(architecture, decoder), - MetricsName: metricsName, - MetricsValue: metricsValue, - }) - } - } - log.Printf("Logs parsed, result: %v", responseRaw) - - response, err := json.Marshal(responseRaw) - if err != nil { - log.Printf("Marshal result in NAS job 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/generalActions.js b/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js index 024f30fb59d..b7b89dede56 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js @@ -28,16 +28,18 @@ export const DELETE_EXPERIMENT_REQUEST = "DELETE_EXPERIMENT_REQUEST"; export const DELETE_EXPERIMENT_FAILURE = "DELETE_EXPERIMENT_FAILURE"; export const DELETE_EXPERIMENT_SUCCESS = "DELETE_EXPERIMENT_SUCCESS"; -export const deleteExperiment = (experimentName) => ({ +export const deleteExperiment = (name, namespace) => ({ type: DELETE_EXPERIMENT_REQUEST, - experimentName, + name, + namespace, }) export const OPEN_DELETE_EXPERIMENT_DIALOG = "OPEN_DELETE_EXPERIMENT_DIALOG"; -export const openDeleteExperimentDialog = (experimentName) => ({ +export const openDeleteExperimentDialog = (name, namespace) => ({ type: OPEN_DELETE_EXPERIMENT_DIALOG, - experimentName, + name, + namespace, }) export const CLOSE_DELETE_EXPERIMENT_DIALOG = "CLOSE_DELETE_EXPERIMENT_DIALOG"; diff --git a/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js b/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js index 0aef7c5fecf..33627b05683 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js @@ -121,6 +121,13 @@ export const changeTrial = (trial) => ({ trial, }) +export const CHANGE_TRIAL_NAMESPACE_HP = "CHANGE_TRIAL_NAMESPACE_HP"; + +export const changeTrialNamespace = (namespace) => ({ + type: CHANGE_TRIAL_NAMESPACE_HP, + namespace, +}) + export const SUBMIT_HP_JOB_REQUEST = "SUBMIT_HP_JOB_REQUEST"; export const SUBMIT_HP_JOB_SUCCESS = "SUBMIT_HP_JOB_SUCCESS"; export const SUBMIT_HP_JOB_FAILURE = "SUBMIT_HP_JOB_FAILURE"; diff --git a/pkg/ui/v1alpha3/frontend/src/actions/hpMonitorActions.js b/pkg/ui/v1alpha3/frontend/src/actions/hpMonitorActions.js index 1439e1634c1..9a5de47316e 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/hpMonitorActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/hpMonitorActions.js @@ -24,9 +24,10 @@ export const FETCH_HP_JOB_INFO_REQUEST = "FETCH_HP_JOB_INFO_REQUEST"; export const FETCH_HP_JOB_INFO_SUCCESS = "FETCH_HP_JOB_INFO_SUCCESS"; export const FETCH_HP_JOB_INFO_FAILURE = "FETCH_HP_JOB_INFO_FAILURE"; -export const fetchHPJobInfo = (experimentName) => ({ +export const fetchHPJobInfo = (name, namespace) => ({ type: FETCH_HP_JOB_INFO_REQUEST, - experimentName + name, + namespace, }) export const FETCH_HP_JOB_TRIAL_INFO_REQUEST = "FETCH_HP_JOB_TRIAL_INFO_REQUEST"; diff --git a/pkg/ui/v1alpha3/frontend/src/components/App.jsx b/pkg/ui/v1alpha3/frontend/src/components/App.jsx index dfa35465a20..e2c727f9575 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/App.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/App.jsx @@ -32,7 +32,7 @@ const App = (props) => { - + diff --git a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx index 3472f39b687..852c952b631 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx @@ -175,7 +175,8 @@ const mapStateToProps = (state) => ({ algorithmName: state[module].algorithmName, algorithmSettings: state[module].algorithmSettings, parameters: state[module].parameters, - 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/HP/Create/Params/Trial.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/Params/Trial.jsx index ad031d15afa..fdbb9604fbc 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/Params/Trial.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/Params/Trial.jsx @@ -12,7 +12,7 @@ import Select from '@material-ui/core/Select'; import TextField from '@material-ui/core/TextField'; import { connect } from 'react-redux'; -import { changeTrial } from '../../../../actions/hpCreateActions'; +import { changeTrial, changeTrialNamespace } from '../../../../actions/hpCreateActions'; import { fetchTrialTemplates } from '../../../../actions/templateActions'; const module = "hpCreate"; @@ -44,6 +44,7 @@ class TrialSpecParam extends React.Component { onTrialNamespaceChange = (event) => { this.props.fetchTrialTemplates(event.target.value); + this.props.changeTrialNamespace(event.target.value); } onTrialChange = (event) => { @@ -118,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/HP/Monitor/HPJobInfo.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobInfo.jsx index a358870880f..01653e4d251 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobInfo.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobInfo.jsx @@ -33,7 +33,8 @@ const styles = theme => ({ class HPJobInfo extends React.Component { componentDidMount() { - this.props.fetchHPJobInfo(this.props.match.params.name); + this.props.fetchHPJobInfo( + this.props.match.params.name, this.props.match.params.namespace); } render () { @@ -52,6 +53,9 @@ class HPJobInfo extends React.Component { Experiment Name: {this.props.match.params.name} + + Experiment Namespace: {this.props.match.params.namespace} +
diff --git a/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobList.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobList.jsx index 5a65ced35d7..6b1b6dd2d89 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobList.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Monitor/HPJobList.jsx @@ -43,8 +43,8 @@ const HPJobList = (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 HPJobList = (props) => { icon = () } return ( - + {icon} - + - + diff --git a/pkg/ui/v1alpha3/frontend/src/components/Menu/DeleteDialog.jsx b/pkg/ui/v1alpha3/frontend/src/components/Menu/DeleteDialog.jsx index fbb5c9b3743..71c06833ed7 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/Menu/DeleteDialog.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/Menu/DeleteDialog.jsx @@ -22,7 +22,7 @@ const DeleteDialog = (props) => { const classes = useStyles(); const onDelete = () => { - props.deleteExperiment(props.deleteExperimentName); + props.deleteExperiment(props.deleteExperimentName, props.deleteExperimentNamespace); } return ( @@ -53,6 +53,7 @@ const DeleteDialog = (props) => { const mapStateToProps = (state) => ({ open: state[module].deleteDialog, deleteExperimentName: state[module].deleteExperimentName, + deleteExperimentNamespace: state[module].deleteExperimentNamespace, }) export default connect(mapStateToProps, { closeDeleteExperimentDialog, deleteExperiment })(DeleteDialog); diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/general.js b/pkg/ui/v1alpha3/frontend/src/reducers/general.js index d0820359025..84fef88682e 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/general.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/general.js @@ -52,7 +52,8 @@ const generalReducer = (state = initialState, action) => { return { ...state, deleteDialog: true, - deleteExperimentName: action.experimentName, + deleteExperimentName: action.name, + deleteExperimentNamespace: action.namespace, } case actions.CLOSE_DELETE_EXPERIMENT_DIALOG: return { diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js b/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js index ad3f8ffe1a0..9631c8c8a44 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js @@ -241,6 +241,11 @@ const hpCreateReducer = (state = initialState, action) => { ...state, trial: action.trial, } + case actions.CHANGE_TRIAL_NAMESPACE_HP: + return { + ...state, + trialNamespace: action.namespace, + } default: return state; } diff --git a/pkg/ui/v1alpha3/frontend/src/sagas/index.js b/pkg/ui/v1alpha3/frontend/src/sagas/index.js index 203208ccb38..8b87a797213 100644 --- a/pkg/ui/v1alpha3/frontend/src/sagas/index.js +++ b/pkg/ui/v1alpha3/frontend/src/sagas/index.js @@ -8,7 +8,7 @@ import * as nasCreateActions from '../actions/nasCreateActions'; import * as generalActions from '../actions/generalActions'; -export const submitYaml = function *() { +export const submitYaml = function* () { while (true) { const action = yield take(generalActions.SUBMIT_YAML_REQUEST); try { @@ -24,7 +24,7 @@ export const submitYaml = function *() { yield put({ type: generalActions.SUBMIT_YAML_FAILURE, message: result.message, - }) + }) } } catch (err) { yield put({ @@ -34,7 +34,7 @@ export const submitYaml = function *() { } } -const goSubmitYaml = function *(yaml) { +const goSubmitYaml = function* (yaml) { try { const data = { yaml @@ -53,13 +53,14 @@ const goSubmitYaml = function *(yaml) { } } -export const deleteExperiment = function *() { +export const deleteExperiment = function* () { while (true) { const action = yield take(generalActions.DELETE_EXPERIMENT_REQUEST); try { const result = yield call( goDeleteExperiment, - action.experimentName + action.name, + action.namespace, ) if (result.status === 200) { yield put({ @@ -68,7 +69,7 @@ export const deleteExperiment = function *() { } else { yield put({ type: generalActions.DELETE_EXPERIMENT_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -78,11 +79,11 @@ export const deleteExperiment = function *() { } } -const goDeleteExperiment = function *(experimentName) { +const goDeleteExperiment = function* (name, namespace) { try { const result = yield call( axios.get, - `/katib/delete_experiment/?experimentName=${experimentName}`, + `/katib/delete_experiment/?experimentName=${name}&namespace=${namespace}`, ) return result } catch (err) { @@ -92,7 +93,7 @@ const goDeleteExperiment = function *(experimentName) { } } -export const submitHPJob = function *() { +export const submitHPJob = function* () { while (true) { const action = yield take(hpCreateActions.SUBMIT_HP_JOB_REQUEST); try { @@ -108,7 +109,7 @@ export const submitHPJob = function *() { yield put({ type: hpCreateActions.SUBMIT_HP_JOB_FAILURE, message: result.message, - }) + }) } } catch (err) { yield put({ @@ -118,7 +119,7 @@ export const submitHPJob = function *() { } } -const goSubmitHPJob = function *(postData) { +const goSubmitHPJob = function* (postData) { try { const data = { postData @@ -137,7 +138,7 @@ const goSubmitHPJob = function *(postData) { } } -export const fetchHPJobs = function *() { +export const fetchHPJobs = function* () { while (true) { const action = yield take(hpMonitorActions.FETCH_HP_JOBS_REQUEST); try { @@ -160,7 +161,7 @@ export const fetchHPJobs = function *() { } else { yield put({ type: hpMonitorActions.FETCH_HP_JOBS_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -170,7 +171,7 @@ export const fetchHPJobs = function *() { } } -const goFetchHPJobs = function *() { +const goFetchHPJobs = function* () { try { const result = yield call( axios.get, @@ -184,13 +185,14 @@ const goFetchHPJobs = function *() { } } -export const fetchHPJobInfo = function *() { +export const fetchHPJobInfo = function* () { while (true) { const action = yield take(hpMonitorActions.FETCH_HP_JOB_INFO_REQUEST); try { const result = yield call( goFetchHPJobInfo, - action.experimentName + action.name, + action.namespace ) if (result.status === 200) { let data = result.data.split("\n").map((line, i) => line.split(',')) @@ -201,7 +203,7 @@ export const fetchHPJobInfo = function *() { } else { yield put({ type: hpMonitorActions.FETCH_HP_JOB_INFO_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -211,11 +213,11 @@ export const fetchHPJobInfo = function *() { } } -const goFetchHPJobInfo = function *(experimentName) { +const goFetchHPJobInfo = function* (name, namespace) { try { const result = yield call( axios.get, - `/katib/fetch_hp_job_info/?experimentName=${experimentName}`, + `/katib/fetch_hp_job_info/?experimentName=${name}&namespace=${namespace}`, ) return result } catch (err) { @@ -225,7 +227,7 @@ const goFetchHPJobInfo = function *(experimentName) { } } -export const fetchHPJobTrialInfo = function *() { +export const fetchHPJobTrialInfo = function* () { while (true) { const action = yield take(hpMonitorActions.FETCH_HP_JOB_TRIAL_INFO_REQUEST); try { @@ -242,7 +244,7 @@ export const fetchHPJobTrialInfo = function *() { } else { yield put({ type: hpMonitorActions.FETCH_HP_JOB_TRIAL_INFO_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -252,7 +254,7 @@ export const fetchHPJobTrialInfo = function *() { } } -const gofetchHPJobTrialInfo = function *(trialName) { +const gofetchHPJobTrialInfo = function* (trialName) { try { const result = yield call( axios.get, @@ -266,7 +268,7 @@ const gofetchHPJobTrialInfo = function *(trialName) { } } -export const submitNASJob = function *() { +export const submitNASJob = function* () { while (true) { const action = yield take(nasCreateActions.SUBMIT_NAS_JOB_REQUEST); try { @@ -282,7 +284,7 @@ export const submitNASJob = function *() { yield put({ type: nasCreateActions.SUBMIT_NAS_JOB_FAILURE, message: result.message, - }) + }) } } catch (err) { yield put({ @@ -292,7 +294,7 @@ export const submitNASJob = function *() { } } -const goSubmitNASJob = function *(postData) { +const goSubmitNASJob = function* (postData) { try { const data = { postData @@ -312,7 +314,7 @@ const goSubmitNASJob = function *(postData) { } -export const fetchNASJobs = function *() { +export const fetchNASJobs = function* () { while (true) { const action = yield take(nasMonitorActions.FETCH_NAS_JOBS_REQUEST); try { @@ -335,7 +337,7 @@ export const fetchNASJobs = function *() { } else { yield put({ type: nasMonitorActions.FETCH_NAS_JOBS_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -345,7 +347,7 @@ export const fetchNASJobs = function *() { } } -const goFetchNASJobs = function *() { +const goFetchNASJobs = function* () { try { const result = yield call( axios.get, @@ -359,7 +361,7 @@ const goFetchNASJobs = function *() { } } -export const fetchNASJobInfo = function *() { +export const fetchNASJobInfo = function* () { while (true) { const action = yield take(nasMonitorActions.FETCH_NAS_JOB_INFO_REQUEST); try { @@ -383,7 +385,7 @@ export const fetchNASJobInfo = function *() { } else { yield put({ type: nasMonitorActions.FETCH_NAS_JOB_INFO_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -393,7 +395,7 @@ export const fetchNASJobInfo = function *() { } } -const goFetchNASJobInfo = function *(experimentName) { +const goFetchNASJobInfo = function* (experimentName) { try { const result = yield call( axios.get, @@ -408,7 +410,7 @@ const goFetchNASJobInfo = function *(experimentName) { } -export const fetchTrialTemplates = function *() { +export const fetchTrialTemplates = function* () { while (true) { const action = yield take(templateActions.FETCH_TRIAL_TEMPLATES_REQUEST); try { @@ -432,7 +434,7 @@ export const fetchTrialTemplates = function *() { } else { yield put({ type: templateActions.FETCH_TRIAL_TEMPLATES_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -442,7 +444,7 @@ export const fetchTrialTemplates = function *() { } } -const goFetchTrialTemplates = function *(namespace) { +const goFetchTrialTemplates = function* (namespace) { try { const result = yield call( axios.get, @@ -456,7 +458,7 @@ const goFetchTrialTemplates = function *(namespace) { } } -export const addTemplate = function *() { +export const addTemplate = function* () { while (true) { const action = yield take(templateActions.ADD_TEMPLATE_REQUEST); try { @@ -484,7 +486,7 @@ export const addTemplate = function *() { } else { yield put({ type: templateActions.ADD_TEMPLATE_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -494,7 +496,7 @@ export const addTemplate = function *() { } } -const goAddTemplate = function *(name, yaml, kind, action) { +const goAddTemplate = function* (name, yaml, kind, action) { try { const data = { name, yaml, kind, action @@ -512,7 +514,7 @@ const goAddTemplate = function *(name, yaml, kind, action) { } } -export const editTemplate = function *() { +export const editTemplate = function* () { while (true) { const action = yield take(templateActions.EDIT_TEMPLATE_REQUEST); try { @@ -540,7 +542,7 @@ export const editTemplate = function *() { } else { yield put({ type: templateActions.EDIT_TEMPLATE_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -550,7 +552,7 @@ export const editTemplate = function *() { } } -const goEditTemplate = function *(name, yaml, kind, action) { +const goEditTemplate = function* (name, yaml, kind, action) { try { const data = { name, yaml, kind, action @@ -568,7 +570,7 @@ const goEditTemplate = function *(name, yaml, kind, action) { } } -export const deleteTemplate = function *() { +export const deleteTemplate = function* () { while (true) { const action = yield take(templateActions.DELETE_TEMPLATE_REQUEST); try { @@ -595,7 +597,7 @@ export const deleteTemplate = function *() { } else { yield put({ type: templateActions.DELETE_TEMPLATE_FAILURE, - }) + }) } } catch (err) { yield put({ @@ -605,7 +607,7 @@ export const deleteTemplate = function *() { } } -const goDeleteTemplate = function *(name, kind, action) { +const goDeleteTemplate = function* (name, kind, action) { try { const data = { name, kind, action @@ -628,7 +630,7 @@ export default function* rootSaga() { fork(fetchTrialTemplates), fork(fetchHPJobs), fork(fetchNASJobs), - fork(addTemplate), + fork(addTemplate), fork(editTemplate), fork(deleteTemplate), fork(submitYaml), diff --git a/pkg/ui/v1alpha3/nas.go b/pkg/ui/v1alpha3/nas.go new file mode 100644 index 00000000000..02bb1c7fde1 --- /dev/null +++ b/pkg/ui/v1alpha3/nas.go @@ -0,0 +1,106 @@ +package v1alpha3 + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strconv" + + 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) { + //enableCors(&w) + jobs, err := k.getExperimentList(consts.DefaultKatibNamespace, JobTypeNAS) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + response, err := json.Marshal(jobs) + if err != nil { + log.Printf("Marshal NAS jobs failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +func (k *KatibUIHandler) FetchNASJobInfo(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + experimentName := r.URL.Query()["experimentName"][0] + + responseRaw := make([]NNView, 0) + var architecture string + var decoder string + + conn, c := k.connectManager() + + defer conn.Close() + + trials, err := k.katibClient.GetTrialList(experimentName) + if err != nil { + log.Printf("GetTrialList from NAS job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Printf("Got Trial List") + + for i, t := range trials.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 NAS job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + metricsName := make([]string, 0) + metricsValue := make([]string, 0) + for _, m := range obsLogResp.ObservationLog.MetricLogs { + metricsName = append(metricsName, m.Metric.Name) + metricsValue = append(metricsValue, m.Metric.Value) + + } + for _, trialParam := range t.Spec.ParameterAssignments { + if trialParam.Name == "architecture" { + architecture = trialParam.Value + } + if trialParam.Name == "nn_config" { + decoder = trialParam.Value + } + } + responseRaw = append(responseRaw, NNView{ + Name: "Generation " + strconv.Itoa(i), + TrialName: t.Name, + Architecture: generateNNImage(architecture, decoder), + MetricsName: metricsName, + MetricsValue: metricsValue, + }) + } + } + log.Printf("Logs parsed, result: %v", responseRaw) + + response, err := json.Marshal(responseRaw) + if err != nil { + log.Printf("Marshal result in NAS job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} diff --git a/pkg/ui/v1alpha3/types.go b/pkg/ui/v1alpha3/types.go index fb1a484da51..7c0784092d6 100644 --- a/pkg/ui/v1alpha3/types.go +++ b/pkg/ui/v1alpha3/types.go @@ -1,6 +1,8 @@ -package ui +package v1alpha3 -import "github.com/kubeflow/katib/pkg/util/v1alpha3/katibclient" +import ( + "github.com/kubeflow/katib/pkg/util/v1alpha3/katibclient" +) const maxMsgSize = 1<<31 - 1 @@ -29,8 +31,9 @@ type Option struct { } type JobView struct { - Name string - Status string + Name string + Status string + Namespace string } type TemplateView struct { @@ -54,3 +57,10 @@ type NNView struct { MetricsName []string MetricsValue []string } + +type JobType string + +const ( + JobTypeHP = "HP" + JobTypeNAS = "NAS" +) diff --git a/pkg/ui/v1alpha3/util.go b/pkg/ui/v1alpha3/util.go index f35c30502ac..289d6b04f10 100644 --- a/pkg/ui/v1alpha3/util.go +++ b/pkg/ui/v1alpha3/util.go @@ -1,8 +1,9 @@ -package ui +package v1alpha3 import ( "encoding/json" "errors" + "log" "net/http" "strconv" "strings" @@ -10,6 +11,32 @@ import ( gographviz "github.com/awalterschulze/gographviz" ) +func (k *KatibUIHandler) getExperimentList(namespace string, typ JobType) ([]JobView, error) { + jobs := make([]JobView, 0) + + el, err := k.katibClient.GetExperimentList(namespace) + if err != nil { + log.Printf("GetExperimentList failed: %v", err) + return nil, err + } + for _, experiment := range el.Items { + if (typ == JobTypeNAS && experiment.Spec.NasConfig != nil) || + (typ == JobTypeHP && experiment.Spec.NasConfig == nil) { + experimentLastCondition, err := experiment.GetLastConditionType() + if err != nil { + log.Printf("GetLastConditionType failed: %v", err) + return nil, err + } + jobs = append(jobs, JobView{ + Name: experiment.Name, + Namespace: experiment.Namespace, + Status: string(experimentLastCondition), + }) + } + } + return jobs, nil +} + func enableCors(w *http.ResponseWriter) { (*w).Header().Set("Content-Type", "text/html; charset=utf-8") (*w).Header().Set("Access-Control-Allow-Origin", "*")