| <!-- |
| ~ Licensed to the Apache Software Foundation (ASF) under one |
| ~ or more contributor license agreements. See the NOTICE file |
| ~ distributed with this work for additional information |
| ~ regarding copyright ownership. The ASF licenses this file |
| ~ to you under the Apache License, Version 2.0 (the |
| ~ "License"); you may not use this file except in compliance |
| ~ with the License. You may obtain a copy of the License at |
| ~ |
| ~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~ |
| ~ Unless required by applicable law or agreed to in writing, |
| ~ software distributed under the License is distributed on an |
| ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| ~ KIND, either express or implied. See the License for the |
| ~ specific language governing permissions and limitations |
| ~ under the License. |
| --> |
| |
| <div> |
| <nz-steps [nzCurrent]="step"> |
| <nz-step nzTitle="First Step"></nz-step> |
| <nz-step nzTitle="Second Step"></nz-step> |
| <nz-step nzTitle="Preview"></nz-step> |
| </nz-steps> |
| </div> |
| <div> |
| <form [formGroup]="experiment"> |
| <div [ngSwitch]="step" style="margin-top: 30px"> |
| <div *ngSwitchCase="0" id="firstStep"> |
| <div class="single-field-group"> |
| <label for="experimentName"> |
| <span class="red-star">*</span> |
| Experiment Name |
| </label> |
| <input |
| nz-input |
| type="text" |
| name="experimentName" |
| id="experimentName" |
| formControlName="experimentName" |
| placeholder="mnist-example" |
| /> |
| </div> |
| <div class="single-field-group"> |
| <label for="description">Description</label> |
| <textarea |
| nz-input |
| [nzAutosize]="{ minRows: 1, maxRows: 4 }" |
| name="description" |
| formControlName="description" |
| id="description" |
| placeholder="Run distributed mnist example" |
| ></textarea> |
| </div> |
| <div class="single-field-group"> |
| <label for="cmd"> |
| <span class="red-star">*</span> |
| Command |
| </label> |
| <textarea |
| nz-input |
| name="cmd" |
| formControlName="cmd" |
| placeholder="python /var/tf_mnist/mnist_with_summaries.py --max_steps=100" |
| id="cmd" |
| [nzAutosize]="{ minRows: 2, maxRows: 4 }" |
| ></textarea> |
| </div> |
| <div class="single-field-group"> |
| <label for="image"> |
| <span class="red-star">*</span> |
| Image |
| </label> |
| <nz-select nzShowSearch [nzDropdownRender]="renderTemplate" name="image" formControlName="image" id="image"> |
| <nz-option *ngFor="let image of imageList" [nzValue]="image" [nzLabel]="image"></nz-option> |
| </nz-select> |
| <ng-template #renderTemplate> |
| <nz-divider></nz-divider> |
| <div class="container"> |
| <input type="text" nz-input #inputElement /> |
| <a class="add-item" (click)="addItem(inputElement)"> |
| <i nz-icon nzType="plus"></i> |
| Add item |
| </a> |
| </div> |
| </ng-template> |
| </div> |
| <div style="margin-bottom: 10px"> |
| <button |
| nz-button |
| style="display: block; margin: auto" |
| id="advancedBtn" |
| nzType="default" |
| (click)="ADVANCED = !ADVANCED" |
| > |
| Advanced |
| <i nz-icon [nzType]="ADVANCED ? 'up' : 'down'"></i> |
| </button> |
| </div> |
| <div *ngIf="ADVANCED" class="single-field-group"> |
| <label for="namespace"> |
| <span class="red-star">*</span> |
| Namespace |
| </label> |
| <nz-select formControlName="namespace" id="namespace"> |
| <nz-option *ngFor="let namespace of nameSpaceList" [nzValue]="namespace" [nzLabel]="namespace"></nz-option> |
| </nz-select> |
| </div> |
| <div *ngIf="ADVANCED" class="single-field-group"> |
| <label for="git-repo">Git repository</label> |
| <nz-input-group [nzSuffix]="suffixTemplateInfo"> |
| <input |
| nz-input |
| type="text" |
| name="git-repo" |
| id="git-repo" |
| placeholder="https://github.com/apache/submarine.git" |
| formControlName="gitRepo" |
| /> |
| </nz-input-group> |
| <ng-template #suffixTemplateInfo> |
| <i nz-icon nz-tooltip nzTitle="Copy git repo to /code/current in container." nzType="info-circle"></i> |
| </ng-template> |
| </div> |
| <div *ngIf="ADVANCED"> |
| <ul formArrayName="envs" class="list-container"> |
| <ng-container *ngFor="let env of envs.controls; index as i"> |
| <li [formGroupName]="i" class="input-group"> |
| <div style="margin-left: 30%"> |
| <label for="key{{ i }}">Key</label> |
| <input nz-input name="key{{ i }}" placeholder="Key" formControlName="key" id="key{{ i }}" /> |
| </div> |
| <div style="margin-left: 0"> |
| <label for="value{{ i }}">Value</label> |
| <input nz-input name="value{{ i }}" placeholder="Value" formControlName="value" id="value{{ i }}" /> |
| </div> |
| <i nz-icon nzType="close-circle" nzTheme="fill" (click)="deleteItem(envs, i)"></i> |
| </li> |
| <div |
| class="alert-message" |
| *ngIf="env.get('key').dirty | logicalAnd: env.get('value').dirty:env.hasError('envMissing')" |
| > |
| {{ env.getError('envMissing') }} |
| </div> |
| <div |
| class="alert-message" |
| *ngIf="env.get('key').dirty | logicalAnd: env.get('key').hasError('duplicateError')" |
| > |
| {{ env.get('key').getError('duplicateError') }} |
| </div> |
| </ng-container> |
| </ul> |
| <button nz-button id="env-btn" style="display: block; margin: auto" nzType="primary" (click)="onCreateEnv()"> |
| Add new environment variable |
| </button> |
| <br /> |
| </div> |
| </div> |
| <div *ngSwitchCase="1" id="secondStep"> |
| <nz-radio-group [(ngModel)]="framework" [ngModelOptions]="{ standalone: true }" id="jobs-container"> |
| <label nz-radio nzValue="Tensorflow" (click)="deleteAllItem(specs); jobTypes = 'Distributed Tensorflow'"> |
| Distributed Tensorflow |
| </label> |
| <label nz-radio nzValue="Pytorch" (click)="deleteAllItem(specs); jobTypes = 'Distributed Pytorch'"> |
| Distributed PyTorch |
| </label> |
| <label |
| nz-radio |
| nzValue="Standalone" |
| (click)="deleteAllItem(specs); onCreateSpec(); jobTypes = 'Standalone Script'" |
| > |
| Standalone Script |
| </label> |
| </nz-radio-group> |
| <label class="pg3-form-label"></label> |
| <button *ngIf="framework !== 'Standalone'" nz-button id="spec-btn" nzType="default" (click)="onCreateSpec()"> |
| Add new spec |
| </button> |
| <ul formArrayName="specs" class="list-container"> |
| <ng-container *ngFor="let spec of specs.controls; index as i"> |
| <li *ngIf="i | indexInRange: currentSpecPage:PAGESIZE" [formGroupName]="i" class="input-group"> |
| <div id="spec{{ i }}" *ngIf="framework !== 'Standalone'"> |
| <label>Spec name</label> |
| <nz-select formControlName="name" nzPlaceHolder="Spec name" [ngSwitch]="framework"> |
| <div *ngSwitchCase="'Tensorflow'"> |
| <nz-option *ngFor="let spec of TF_SPECNAMES" [nzValue]="spec" [nzLabel]="spec"></nz-option> |
| </div> |
| <div *ngSwitchCase="'Pytorch'"> |
| <nz-option *ngFor="let spec of PYTORCH_SPECNAMES" [nzValue]="spec" [nzLabel]="spec"></nz-option> |
| </div> |
| </nz-select> |
| </div> |
| <div *ngIf="framework !== 'Standalone'"> |
| <label>Number of Replica</label> |
| <input |
| nz-input |
| name="replica{{ i }}" |
| type="number" |
| placeholder="number of replica" |
| formControlName="replicas" |
| /> |
| </div> |
| <div> |
| <label>Number of cpu</label> |
| <input nz-input name="cpu{{ i }}" type="number" placeholder="number of cpu" formControlName="cpus" /> |
| </div> |
| <div> |
| <label>Number of gpu</label> |
| <input nz-input name="gpu{{ i }}" type="number" placeholder="number of gpu" formControlName="gpus" /> |
| </div> |
| <div id="memory{{ i }}"> |
| <label>Memory</label> |
| <div formGroupName="memory" class="memory-input-group"> |
| <input |
| nz-input |
| name="memory{{ i }}" |
| type="number" |
| step="1024" |
| placeholder="Enter number" |
| formControlName="num" |
| /> |
| <nz-select formControlName="unit"> |
| <nz-option *ngFor="let unit of MEMORY_UNITS" [nzValue]="unit" [nzLabel]="unit"></nz-option> |
| </nz-select> |
| </div> |
| </div> |
| <i nz-icon nzType="close-circle" nzTheme="fill" class="delete-icon" (click)="deleteItem(specs, i)"></i> |
| </li> |
| <div |
| class="alert-message" |
| *ngIf="spec.get('name').dirty | logicalAnd: spec.get('memory').dirty:spec.hasError('specError')" |
| > |
| {{ spec.getError('specError') }} |
| </div> |
| |
| <div |
| class="alert-message" |
| *ngIf="spec.get('name').dirty | logicalAnd: spec.get('name').hasError('duplicateError')" |
| > |
| {{ spec.get('name').getError('duplicateError') }} |
| </div> |
| <div |
| class="alert-message" |
| *ngIf="spec.get('memory').dirty | logicalAnd: spec.get('memory').hasError('memoryPatternError')" |
| > |
| {{ spec.get('memory').getError('memoryPatternError') }} |
| </div> |
| <div |
| class="alert-message" |
| *ngIf="spec.get('replicas').dirty | logicalAnd: spec.get('replicas').hasError('min')" |
| > |
| replicas must be at least 1 |
| </div> |
| <div class="alert-message" *ngIf="spec.get('cpus').dirty | logicalAnd: spec.get('cpus').hasError('min')"> |
| cpus must be at least 1 |
| </div> |
| <div class="alert-message" *ngIf="spec.get('gpus').dirty | logicalAnd: spec.get('gpus').hasError('min')"> |
| cpus must be at least 0 |
| </div> |
| </ng-container> |
| </ul> |
| <nz-pagination |
| [(nzPageIndex)]="currentSpecPage" |
| [nzPageSize]="PAGESIZE" |
| [nzTotal]="specs.controls.length" |
| nzSimple |
| ></nz-pagination> |
| </div> |
| <div *ngSwitchCase="2" id="previewPage"> |
| <nz-descriptions nzTitle="{{ jobTypes }}" nzBordered [nzColumn]="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 2, xs: 1 }"> |
| <nz-descriptions-item nzTitle="Name">{{ finalExperimentSpec.meta.name }}</nz-descriptions-item> |
| <nz-descriptions-item nzTitle="Namespace">{{ finalExperimentSpec.meta.namespace }}</nz-descriptions-item> |
| <nz-descriptions-item nzTitle="Command" [nzSpan]="2"> |
| {{ finalExperimentSpec.meta.cmd }} |
| </nz-descriptions-item> |
| <nz-descriptions-item nzTitle="Image" [nzSpan]="2"> |
| {{ finalExperimentSpec.environment.image }} |
| </nz-descriptions-item> |
| <nz-descriptions-item nzTitle="Environment Variables" [nzSpan]="2"> |
| <span *ngFor="let item of finalExperimentSpec.meta.envVars | keyvalue; let isLast = last"> |
| {{ item.key }}={{ item.value }}{{ isLast ? '' : ', ' }} |
| </span> |
| </nz-descriptions-item> |
| <div *ngFor="let item of finalExperimentSpec.spec | keyvalue"> |
| <nz-descriptions-item nzTitle="{{ item.key }}" [nzSpan]="2"> |
| {{ item.value.resources }} |
| </nz-descriptions-item> |
| </div> |
| <div *ngIf="finalExperimentSpec.code"> |
| <nz-descriptions-item nzTitle="Git repository">{{ finalExperimentSpec.code.url }}</nz-descriptions-item> |
| </div> |
| </nz-descriptions> |
| </div> |
| </div> |
| </form> |
| </div> |