Form validation for Qemu

This commit is contained in:
Piotr Pekala 2019-01-29 06:53:18 -08:00
parent 5a21d117ca
commit a6b4c4a9c8
8 changed files with 224 additions and 81 deletions

View File

@ -102,6 +102,7 @@ import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templ
import { VpcsService } from './services/vpcs.service'; import { VpcsService } from './services/vpcs.service';
import { AddVpcsTemplateComponent } from './components/preferences/vpcs/add-vpcs-template/add-vpcs-template.component'; import { AddVpcsTemplateComponent } from './components/preferences/vpcs/add-vpcs-template/add-vpcs-template.component';
import { VpcsTemplateDetailsComponent } from './components/preferences/vpcs/vpcs-template-details/vpcs-template-details.component'; import { VpcsTemplateDetailsComponent } from './components/preferences/vpcs/vpcs-template-details/vpcs-template-details.component';
import { TemplateMocksService } from './services/template-mocks.service';
if (environment.production) { if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@ -212,7 +213,8 @@ if (environment.production) {
ToolsService, ToolsService,
ServerSettingsService, ServerSettingsService,
QemuService, QemuService,
VpcsService VpcsService,
TemplateMocksService
], ],
entryComponents: [ entryComponents: [
AddServerDialogComponent, AddServerDialogComponent,

View File

@ -8,60 +8,87 @@
<div class="example-container mat-elevation-z8"> <div class="example-container mat-elevation-z8">
<mat-vertical-stepper [linear]="true"> <mat-vertical-stepper [linear]="true">
<mat-step label="QEMU VM Name"> <mat-step label="QEMU VM Name">
<mat-form-field class="form-field"> <form [formGroup]="firstStepForm">
<input matInput type="text" [(ngModel)]="templateName" placeholder="Please choose a descriptive name for your new QEMU virtual machine"/> <mat-form-field class="form-field">
</mat-form-field><br/> <input
<mat-checkbox> matInput type="text"
This is a legacy ASA VM [(ngModel)]="qemuTemplate.name"
</mat-checkbox> formControlName="templateName"
placeholder="Please choose a descriptive name for your new QEMU virtual machine"/>
</mat-form-field><br/>
<mat-checkbox>
This is a legacy ASA VM
</mat-checkbox>
</form>
</mat-step> </mat-step>
<mat-step label="QEMU binary and memory"> <mat-step label="QEMU binary and memory">
<mat-form-field class="form-field"> <form [formGroup]="secondStepForm">
<mat-select placeholder="Qemu binary" [(ngModel)]="selectedBinary"> <mat-form-field class="form-field">
<mat-option *ngFor="let binary of qemuBinaries" [value]="binary"> <mat-select
{{binary.path}} placeholder="Qemu binary"
</mat-option> [(ngModel)]="selectedBinary"
</mat-select> [ngModelOptions]="{standalone: true}" >
</mat-form-field><br/> <mat-option *ngFor="let binary of qemuBinaries" [value]="binary">
<mat-form-field class="form-field"> {{binary.path}}
<input matInput type="number" placeholder="RAM" [(ngModel)]="ramMemory"/> </mat-option>
</mat-form-field> </mat-select>
</mat-form-field><br/>
<mat-form-field class="form-field">
<input
matInput type="number"
placeholder="RAM"
[(ngModel)]="ramMemory"
formControlName="ramMemory"/>
</mat-form-field>
</form>
</mat-step> </mat-step>
<mat-step label="Console type"> <mat-step label="Console type">
<mat-form-field class="form-field"> <form [formGroup]="thirdStepForm">
<mat-select placeholder="Console type" [(ngModel)]="selectedConsoleType"> <mat-form-field class="form-field">
<mat-option *ngFor="let type of consoleTypes" [value]="type"> <mat-select
{{type}} placeholder="Console type"
</mat-option> [(ngModel)]="qemuTemplate.console_type"
</mat-select> [ngModelOptions]="{standalone: true}" >
</mat-form-field> <mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
</mat-step> </mat-step>
<mat-step label="Disk image"> <mat-step label="Disk image">
<mat-radio-group class="radio-group"> <form [formGroup]="fourthStepForm">
<mat-radio-button class="radio-button" value="1" (click)="setDiskImage('existingImage')" checked>Existing image</mat-radio-button> <mat-radio-group class="radio-group">
<mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button> <mat-radio-button class="radio-button" value="1" (click)="setDiskImage('existingImage')" checked>Existing image</mat-radio-button>
</mat-radio-group> <mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button>
<mat-select *ngIf="!newImageSelected" placeholder="Disk image (hda)" [(ngModel)]="selectedImage"> </mat-radio-group>
<mat-option *ngFor="let image of qemuImages" [value]="image"> <mat-select
{{image.filename}} *ngIf="!newImageSelected"
</mat-option> placeholder="Disk image (hda)"
</mat-select> [ngModelOptions]="{standalone: true}"
<div *ngIf="newImageSelected"> [(ngModel)]="selectedImage">
<input <mat-option *ngFor="let image of qemuImages" [value]="image">
type="file" {{image.filename}}
accept=".qcow2" </mat-option>
#file </mat-select>
class="nonvisible" <div *ngIf="newImageSelected">
(change)="uploadImageFile($event)"/>
<button mat-raised-button color="primary" (click)="file.click()" class="file-button">Browse</button>
<mat-form-field class="file-name-form-field">
<input <input
matInput type="file"
type="text" accept=".qcow2"
[(ngModel)]="chosenImage" #file
placeholder="Please enter name"/> class="nonvisible"
</mat-form-field> (change)="uploadImageFile($event)"/>
</div> <button mat-raised-button color="primary" (click)="file.click()" class="file-button">Browse</button>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="chosenImage"
formControlName="fileName"
placeholder="Please enter name"/>
</mat-form-field>
</div>
</form>
<div class="buttons-bar"><button mat-raised-button color="primary" (click)="addTemplate()">Add template</button></div> <div class="buttons-bar"><button mat-raised-button color="primary" (click)="addTemplate()">Add template</button></div>
</mat-step> </mat-step>
</mat-vertical-stepper> </mat-vertical-stepper>

View File

@ -8,6 +8,9 @@ import { QemuBinary } from '../../../../models/qemu/qemu-binary';
import { QemuImage } from '../../../../models/qemu/qemu-image'; import { QemuImage } from '../../../../models/qemu/qemu-image';
import { ToasterService } from '../../../../services/toaster.service'; import { ToasterService } from '../../../../services/toaster.service';
import { QemuTemplate } from '../../../../models/templates/qemu-template'; import { QemuTemplate } from '../../../../models/templates/qemu-template';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
import { v4 as uuid } from 'uuid';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
@Component({ @Component({
@ -17,24 +20,46 @@ import { QemuTemplate } from '../../../../models/templates/qemu-template';
}) })
export class AddQemuVmTemplateComponent implements OnInit { export class AddQemuVmTemplateComponent implements OnInit {
server: Server; server: Server;
templateName: string;
qemuBinaries: QemuBinary[] = []; qemuBinaries: QemuBinary[] = [];
selectedBinary: QemuBinary; selectedBinary: QemuBinary;
ramMemory: number; ramMemory: number;
consoleTypes: string[] = ['telnet', 'vnc', 'spice', 'spice+agent', 'none']; consoleTypes: string[] = ['telnet', 'vnc', 'spice', 'spice+agent', 'none'];
selectedConsoleType: string; newImageSelected: boolean = false;;
newImageSelected: boolean;
qemuImages: QemuImage[] = []; qemuImages: QemuImage[] = [];
selectedImage: QemuImage; selectedImage: QemuImage;
chosenImage: string; chosenImage: string = '';
qemuTemplate: QemuTemplate;
firstStepForm: FormGroup;
secondStepForm: FormGroup;
thirdStepForm: FormGroup;
fourthStepForm: FormGroup;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private serverService: ServerService, private serverService: ServerService,
private qemuService: QemuService, private qemuService: QemuService,
private toasterService: ToasterService, private toasterService: ToasterService,
private router: Router private router: Router,
) {} private formBuilder: FormBuilder,
private templateMocksService: TemplateMocksService
) {
this.qemuTemplate = new QemuTemplate();
this.firstStepForm = this.formBuilder.group({
templateName: new FormControl('', Validators.required)
});
this.secondStepForm = this.formBuilder.group({
ramMemory: new FormControl('', Validators.required)
});
this.thirdStepForm = this.formBuilder.group({});
this.fourthStepForm = this.formBuilder.group({
fileName: new FormControl('', Validators.required)
});
}
ngOnInit() { ngOnInit() {
this.route.paramMap this.route.paramMap
@ -46,9 +71,15 @@ export class AddQemuVmTemplateComponent implements OnInit {
) )
.subscribe((server: Server) => { .subscribe((server: Server) => {
this.server = server; this.server = server;
this.templateMocksService.getQemuTemplate().subscribe((qemuTemplate: QemuTemplate) => {
this.qemuTemplate = qemuTemplate;
})
this.qemuService.getBinaries(server).subscribe((qemuBinaries: QemuBinary[]) => { this.qemuService.getBinaries(server).subscribe((qemuBinaries: QemuBinary[]) => {
this.qemuBinaries = qemuBinaries; this.qemuBinaries = qemuBinaries;
}); });
this.qemuService.getImages(server).subscribe((qemuImages: QemuImage[]) => { this.qemuService.getImages(server).subscribe((qemuImages: QemuImage[]) => {
this.qemuImages = qemuImages; this.qemuImages = qemuImages;
}); });
@ -64,15 +95,18 @@ export class AddQemuVmTemplateComponent implements OnInit {
} }
addTemplate() { addTemplate() {
if (!(this.templateName && this.selectedBinary && this.ramMemory && this.selectedConsoleType && if (!this.firstStepForm.invalid && !this.secondStepForm.invalid && !this.thirdStepForm.invalid
(this.selectedImage || this.chosenImage))) { && (this.selectedImage || this.chosenImage)) {
let qemuTemplate = new QemuTemplate(); this.qemuTemplate.ram = this.ramMemory;
qemuTemplate.adapter_type = "e1000"; this.qemuTemplate.qemu_path = this.selectedBinary.path;
qemuTemplate.adapters = 1; if (this.newImageSelected) {
qemuTemplate.boot_priority = "c"; this.qemuTemplate.hda_disk_image = this.chosenImage;
qemuTemplate.category = "guest"; } else {
qemuTemplate.name = this.templateName; this.qemuTemplate.hda_disk_image = this.selectedImage.path;
this.qemuService.addTemplate(this.server, qemuTemplate).subscribe((template: QemuTemplate) => { }
this.qemuTemplate.template_id = uuid();
this.qemuService.addTemplate(this.server, this.qemuTemplate).subscribe((template: QemuTemplate) => {
this.router.navigate(['/server', this.server.id, 'preferences', 'qemu', 'templates']); this.router.navigate(['/server', this.server.id, 'preferences', 'qemu', 'templates']);
}); });
} else { } else {

View File

@ -7,6 +7,7 @@ import { VpcsService } from '../../../../services/vpcs.service';
import { VpcsTemplate } from '../../../../models/templates/vpcs-template'; import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
import { ToasterService } from '../../../../services/toaster.service'; import { ToasterService } from '../../../../services/toaster.service';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
@Component({ @Component({
@ -23,7 +24,8 @@ export class AddVpcsTemplateComponent implements OnInit {
private serverService: ServerService, private serverService: ServerService,
private vpcsService: VpcsService, private vpcsService: VpcsService,
private router: Router, private router: Router,
private toasterService: ToasterService private toasterService: ToasterService,
private templateMocksService: TemplateMocksService
) {} ) {}
ngOnInit() { ngOnInit() {
@ -41,19 +43,15 @@ export class AddVpcsTemplateComponent implements OnInit {
addTemplate() { addTemplate() {
if (this.templateName) { if (this.templateName) {
let vpcsTemplate: VpcsTemplate = { let vpcsTemplate: VpcsTemplate;
base_script_file: 'vpcs_base_config.txt',
builtin: false, this.templateMocksService.getVpcsTemplate().subscribe((template: VpcsTemplate) => {
category: 'guest', vpcsTemplate = template;
compute_id: 'local', });
console_auto_start: false,
console_type: 'telnet', vpcsTemplate.template_id = uuid(),
default_name_format: 'PC{0}', vpcsTemplate.name = this.templateName,
name: this.templateName,
symbol: ':/symbols/vpcs_guest.svg',
template_id: uuid(),
template_type: 'vpcs'
};
this.vpcsService.addTemplate(this.server, vpcsTemplate).subscribe((vpcsTemplate) => { this.vpcsService.addTemplate(this.server, vpcsTemplate).subscribe((vpcsTemplate) => {
this.router.navigate(['/server', this.server.id, 'preferences', 'vpcs', 'templates']); this.router.navigate(['/server', this.server.id, 'preferences', 'vpcs', 'templates']);
}); });

View File

@ -35,20 +35,28 @@
placeholder="Symbol"> placeholder="Symbol">
</mat-form-field> </mat-form-field>
<mat-form-field class="row"> <mat-form-field class="row">
<mat-select placeholder="Category" [ngModelOptions]="{standalone: true}" [(ngModel)]="vpcsTemplate.category"> <mat-select
placeholder="Category"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="vpcsTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]"> <mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}} {{category[0]}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field class="select"> <mat-form-field class="select">
<mat-select placeholder="Console type" [ngModelOptions]="{standalone: true}" [(ngModel)]="vpcsTemplate.console_type"> <mat-select
placeholder="Console type"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="vpcsTemplate.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type"> <mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}} {{type}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="vpcsTemplate.console_auto_start"> <mat-checkbox
[ngModelOptions]="{standalone: true}"
[(ngModel)]="vpcsTemplate.console_auto_start">
Auto start console Auto start console
</mat-checkbox> </mat-checkbox>
</form> </form>

View File

@ -53,7 +53,7 @@ export class VpcsTemplateDetailsComponent implements OnInit {
} }
onSave() { onSave() {
if( this.inputForm.invalid) { if (this.inputForm.invalid) {
this.toasterService.error(`Fill all required fields`); this.toasterService.error(`Fill all required fields`);
} else { } else {
this.vpcsService.saveTemplate(this.server, this.vpcsTemplate).subscribe((vpcsTemaple: VpcsTemplate) => { this.vpcsService.saveTemplate(this.server, this.vpcsTemplate).subscribe((vpcsTemaple: VpcsTemplate) => {

View File

@ -0,0 +1,74 @@
import { Injectable } from '@angular/core';
import { QemuTemplate } from '../models/templates/qemu-template';
import { VpcsTemplate } from '../models/templates/vpcs-template';
import { Observable, of } from 'rxjs';
@Injectable()
export class TemplateMocksService {
getQemuTemplate() : Observable<QemuTemplate> {
let template : QemuTemplate = {
adapter_type: 'e1000',
adapters: 4,
bios_image: '',
boot_priority: 'c',
builtin: false,
category: 'guest',
cdrom_image: '',
compute_id: 'local',
console_auto_start: false,
console_type: 'telnet',
cpu_throttling: 0,
cpus: 1,
custom_adapters: [],
default_name_format: '{name}-{0}',
first_port_name: '',
hda_disk_image: '',
hda_disk_interface: 'ide',
hdb_disk_image: '',
hdb_disk_interface: 'ide',
hdc_disk_image: '',
hdc_disk_interface: 'ide',
hdd_disk_image: '',
hdd_disk_interface: 'ide',
initrd: '',
kernel_command_line: '',
kernel_image: '',
legacy_networking: false,
linked_clone: true,
mac_address: '',
name: '',
on_close: 'power_off',
options: '-nographic',
platform: '',
port_name_format: 'Ethernet{0}',
port_segment_size: 0,
process_priority: 'normal',
qemu_path: '',
ram: 256,
symbol: ':/symbols/qemu_guest.svg',
template_id: '',
template_type: 'qemu',
usage: ''
}
return of(template);
}
getVpcsTemplate() : Observable<VpcsTemplate> {
let template: VpcsTemplate = {
base_script_file: 'vpcs_base_config.txt',
builtin: false,
category: 'guest',
compute_id: 'local',
console_auto_start: false,
console_type: 'telnet',
default_name_format: 'PC{0}',
name: '',
symbol: ':/symbols/vpcs_guest.svg',
template_id: '',
template_type: 'vpcs'
}
return of(template);
}
}