blob: fa32e8e3fd00a751e5975a49d1aebd169a647b48 [file] [log] [blame]
/*
* 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.
*/
import { Component, OnInit, Inject } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { Project } from '../../../administration/project/project.component';
import { UserResourceService, ProjectService } from '../../../core/services';
import { CheckUtils, SortUtils, HTTP_STATUS_CODES, PATTERNS, HelpUtils } from '../../../core/util';
import { DICTIONARY } from '../../../../dictionary/global.dictionary';
import { CLUSTER_CONFIGURATION } from '../../computational/computational-resource-create-dialog/cluster-configuration-templates';
import { tap } from 'rxjs/operators';
import { timer } from 'rxjs';
@Component({
selector: 'create-environment',
templateUrl: 'create-environment.component.html',
styleUrls: ['./create-environment.component.scss']
})
export class ExploratoryEnvironmentCreateComponent implements OnInit {
readonly DICTIONARY = DICTIONARY;
public createExploratoryForm: FormGroup;
public projectExploratories: {};
projects: Project[] = [];
templates = [];
endpoints: Array<String> = [];
currentTemplate: any;
shapes = [] || {};
resourceGrid: any;
images: Array<any>;
selectedImage: any;
maxNotebookLength: number = 14;
maxCustomTagLength: number = 63;
public areShapes: boolean;
public selectedCloud: string = '';
public gpuCount: Array<number>;
public gpuTypes: Array<string> = [];
public addSizeToGpuType = HelpUtils.addSizeToGpuType;
public additionalParams = {
configurationNode: false,
gpu: false,
};
constructor(
@Inject(MAT_DIALOG_DATA) public data: any,
public toastr: ToastrService,
public dialogRef: MatDialogRef<ExploratoryEnvironmentCreateComponent>,
private userResourceService: UserResourceService,
private _fb: FormBuilder,
private projectService: ProjectService
) {
this.resourceGrid = data;
}
ngOnInit() {
this.getNamesByProject();
this.getUserProjects();
this.initFormModel();
this.createExploratoryForm.get('project').valueChanges.subscribe(v => {
if ( this.createExploratoryForm.controls.name.value) {
this.createExploratoryForm.get('name').updateValueAndValidity();
}
});
}
public getProjects() {
this.projectService.getProjectsList().subscribe((projects: any) => this.projects = projects);
}
public getUserProjects() {
this.projectService.getUserProjectsList(true).subscribe((projects: any) => {
this.projects = projects;
const activeProject = projects.find(item => item.name === this.resourceGrid.activeProject);
if (this.resourceGrid.activeProject && activeProject) {
this.setEndpoints(activeProject);
this.createExploratoryForm.controls['project'].setValue(activeProject.name);
}
});
}
public setImage(image) {
this.selectedImage = image;
timer(500).subscribe(_ => {
document.querySelector('#buttons').scrollIntoView({ block: 'center', behavior: 'smooth' });
});
}
public setEndpoints(project) {
const controls = ['endpoint', 'version', 'shape', 'gpu_type', 'gpu_count'];
this.resetSelections(controls);
this.endpoints = project.endpoints
.filter(e => e.status === 'RUNNING')
.map(e => e.name);
}
public resetSelections(controls: Array<string>) {
if (this.images) this.images = [];
if (this.selectedCloud) this.selectedCloud = '';
if (this.additionalParams.gpu) {
this.additionalParams.gpu = !this.additionalParams.gpu;
this.createExploratoryForm.controls['gpu_enabled'].setValue(this.additionalParams.gpu);
}
this.selectedImage = null;
this.areShapes = false;
this.templates = [];
this.currentTemplate = [];
controls.forEach(control => {
this.createExploratoryForm.controls[control].setValue(null);
});
}
public getTemplates(project, endpoint) {
const controls = ['version', 'shape'];
this.resetSelections(controls);
const endpoints = this.data.environments.find(env => env.project === project).endpoints;
this.selectedCloud = endpoints.find(endp => endp.name === endpoint).cloudProvider.toLowerCase();
this.userResourceService.getExploratoryTemplates(project, endpoint)
.pipe(tap(results => {
results.sort((a, b) =>
(a.exploratory_environment_versions[0].template_name > b.exploratory_environment_versions[0].template_name) ?
1 : -1);
}))
.subscribe(templates => {
this.templates = templates;
}
);
}
public getShapes(template) {
this.selectedImage = null;
const controls = ['notebook_image_name', 'shape', 'gpu_type', 'gpu_count'];
controls.forEach(control => {
this.createExploratoryForm.controls[control].setValue(null);
if (control !== 'shape') {
this.createExploratoryForm.controls[control].clearValidators();
this.createExploratoryForm.controls[control].updateValueAndValidity();
}
});
if (this.additionalParams.gpu) {
this.additionalParams.gpu = !this.additionalParams.gpu;
this.createExploratoryForm.controls['gpu_enabled'].setValue(this.additionalParams.gpu);
}
if (this.selectedCloud === 'gcp' && template?.image === 'docker.datalab-deeplearning') {
this.createExploratoryForm.controls['notebook_image_name'].setValidators([Validators.required]);
this.createExploratoryForm.controls['notebook_image_name'].updateValueAndValidity();
}
if (this.selectedCloud === 'gcp' &&
(template?.image === 'docker.datalab-jupyter' ||
template?.image === 'docker.datalab-deeplearning' ||
template?.image === 'docker.datalab-tensor')) {
this.gpuTypes = template?.computationGPU ? HelpUtils.sortGpuTypes(template.computationGPU) : [];
if(template?.image === 'docker.datalab-tensor' || template?.image === 'docker.datalab-deeplearning') {
this.addGpuFields();
}
}
this.currentTemplate = template;
const allowed: any = ['GPU optimized'];
if (template.exploratory_environment_versions[0].template_name.toLowerCase().indexOf('tensorflow') === -1
&& template.exploratory_environment_versions[0].template_name.toLowerCase().indexOf('deeplearning') === -1
&& template.exploratory_environment_versions[0].template_name.toLowerCase().indexOf('deep learning') === -1
&& template.exploratory_environment_versions[0].template_name.toLowerCase().indexOf('data science') === -1
) {
const filtered = Object.keys(
SortUtils.shapesSort(template.exploratory_environment_shapes))
.filter(key => !(allowed.includes(key)))
.reduce((obj, key) => {
obj[key] = template.exploratory_environment_shapes[key];
return obj;
}, {});
template.exploratory_environment_shapes.computation_resources_shapes = filtered;
this.shapes = SortUtils.shapesSort(template.exploratory_environment_shapes.computation_resources_shapes);
this.getImagesList();
} else {
this.shapes = SortUtils.shapesSort(template.exploratory_environment_shapes);
this.getImagesList();
}
this.areShapes = !!Object.keys(this.shapes).length;
}
public createExploratoryEnvironment(data) {
const parameters: any = {
image: this.currentTemplate.image,
template_name: this.currentTemplate.exploratory_environment_versions[0].template_name
};
if (!data.notebook_image_name
&& this.currentTemplate.image === 'docker.datalab-deeplearning'
&& this.selectedCloud === 'aws' || this.selectedCloud === 'azure') {
data.notebook_image_name = this.currentTemplate.exploratory_environment_versions[0].version;
}
data.cluster_config = data.cluster_config ? JSON.parse(data.cluster_config) : null;
this.userResourceService.createExploratoryEnvironment({ ...parameters, ...data }).subscribe((response: any) => {
if (response.status === HTTP_STATUS_CODES.OK) this.dialogRef.close();
}, error => this.toastr.error(error.message || 'Exploratory creation failed!', 'Oops!'));
}
public getNamesByProject() {
this.userResourceService.getProjectByExploratoryEnvironment().subscribe(responce => {
this.projectExploratories = responce;
});
}
public selectConfiguration() {
this.additionalParams.configurationNode = !this.additionalParams.configurationNode;
if (this.additionalParams.configurationNode) {
const value = (this.additionalParams.configurationNode && this.createExploratoryForm)
? JSON.stringify(CLUSTER_CONFIGURATION.SPARK, undefined, 2) : '';
timer(500).subscribe(_ => {
document.querySelector('#buttons').scrollIntoView({ block: 'start', behavior: 'smooth' });
});
this.createExploratoryForm.controls['cluster_config'].setValue(value);
}
}
public addGpuFields() {
this.additionalParams.gpu = !this.additionalParams.gpu;
this.createExploratoryForm.controls['gpu_enabled'].setValue(this.additionalParams.gpu);
const controls = ['gpu_type', 'gpu_count'];
if (!this.additionalParams.gpu) {
controls.forEach(control => {
this.createExploratoryForm.controls[control].setValue(null);
this.createExploratoryForm.controls[control].clearValidators();
this.createExploratoryForm.controls[control].updateValueAndValidity();
});
} else {
controls.forEach(control => {
this.createExploratoryForm.controls[control].setValidators([Validators.required]);
this.createExploratoryForm.controls[control].updateValueAndValidity();
});
timer(500).subscribe(_ => {
document.querySelector('#buttons').scrollIntoView({ block: 'center', behavior: 'smooth' });
});
}
}
public setInstanceSize() {
const controls = ['gpu_type', 'gpu_count'];
controls.forEach(control => {
this.createExploratoryForm.controls[control].setValue(null);
});
}
public setCount(type: any, gpuType: any): void {
if (gpuType && this.currentTemplate.image === 'docker.datalab-deeplearning') {
this.additionalParams.gpu = true;
this.createExploratoryForm.controls['gpu_enabled'].setValue(this.additionalParams.gpu);
this.createExploratoryForm.controls['gpu_count'].setValidators([Validators.required]);
this.createExploratoryForm.controls['gpu_count'].updateValueAndValidity();
}
// if (type === 'master') {
this.gpuCount = [1, 2, 4];
// } else {
// const slaveShape = this.resourceForm.controls['shape_slave'].value;
// this.slaveGPUcount = HelpUtils.setGPUCount(slaveShape, gpuType);
// }
}
private initFormModel(): void {
this.createExploratoryForm = this._fb.group({
project: ['', Validators.required],
endpoint: ['', Validators.required],
version: ['', Validators.required],
notebook_image_name: [''],
shape: ['', Validators.required],
name: ['', [
Validators.required,
Validators.pattern(PATTERNS.namePattern),
Validators.maxLength(this.maxNotebookLength),
this.checkDuplication.bind(this)
]],
cluster_config: ['', [this.validConfiguration.bind(this)]],
custom_tag: ['', [
Validators.pattern(PATTERNS.namePattern),
Validators.maxLength(this.maxCustomTagLength)
]],
gpu_type: [null],
gpu_count: [null],
gpu_enabled: [false]
});
}
private getImagesList() {
this.userResourceService.getUserImages(this.currentTemplate.image, this.createExploratoryForm.controls['project'].value,
this.createExploratoryForm.controls['endpoint'].value)
.subscribe(
(res: any) => {
this.images = res.filter(el => el.status === 'CREATED');
if(this.selectedCloud === 'gcp' && this.currentTemplate.image === 'docker.datalab-deeplearning') {
this.currentTemplate.exploratory_environment_images = this.currentTemplate.exploratory_environment_images.map(image => {
return {name: image['Image family'] ?? image.name, description: image['Description'] ?? image.description}
});
this.images.push(...this.currentTemplate.exploratory_environment_images);
}
},
error => this.toastr.error(error.message || 'Images list loading failed!', 'Oops!')
);
}
private checkDuplication(control) {
if (this.createExploratoryForm
&& this.createExploratoryForm.controls.project.value
&& this.resourceGrid.containsNotebook(control.value, this.projectExploratories[this.createExploratoryForm.controls.project.value]))
return { duplication: true };
}
private validConfiguration(control) {
if (this.additionalParams.configurationNode)
return this.additionalParams.configurationNode
? (control.value && control.value !== null && CheckUtils.isJSON(control.value) ? null : { valid: false })
: null;
}
}