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 { 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 { TemplateMocksService } from './services/template-mocks.service';
if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@ -212,7 +213,8 @@ if (environment.production) {
ToolsService,
ServerSettingsService,
QemuService,
VpcsService
VpcsService,
TemplateMocksService
],
entryComponents: [
AddServerDialogComponent,

View File

@ -8,60 +8,87 @@
<div class="example-container mat-elevation-z8">
<mat-vertical-stepper [linear]="true">
<mat-step label="QEMU VM Name">
<mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="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 [formGroup]="firstStepForm">
<mat-form-field class="form-field">
<input
matInput type="text"
[(ngModel)]="qemuTemplate.name"
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 label="QEMU binary and memory">
<mat-form-field class="form-field">
<mat-select placeholder="Qemu binary" [(ngModel)]="selectedBinary">
<mat-option *ngFor="let binary of qemuBinaries" [value]="binary">
{{binary.path}}
</mat-option>
</mat-select>
</mat-form-field><br/>
<mat-form-field class="form-field">
<input matInput type="number" placeholder="RAM" [(ngModel)]="ramMemory"/>
</mat-form-field>
<form [formGroup]="secondStepForm">
<mat-form-field class="form-field">
<mat-select
placeholder="Qemu binary"
[(ngModel)]="selectedBinary"
[ngModelOptions]="{standalone: true}" >
<mat-option *ngFor="let binary of qemuBinaries" [value]="binary">
{{binary.path}}
</mat-option>
</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 label="Console type">
<mat-form-field class="form-field">
<mat-select placeholder="Console type" [(ngModel)]="selectedConsoleType">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<form [formGroup]="thirdStepForm">
<mat-form-field class="form-field">
<mat-select
placeholder="Console type"
[(ngModel)]="qemuTemplate.console_type"
[ngModelOptions]="{standalone: true}" >
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
</mat-step>
<mat-step label="Disk image">
<mat-radio-group class="radio-group">
<mat-radio-button class="radio-button" value="1" (click)="setDiskImage('existingImage')" checked>Existing image</mat-radio-button>
<mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button>
</mat-radio-group>
<mat-select *ngIf="!newImageSelected" placeholder="Disk image (hda)" [(ngModel)]="selectedImage">
<mat-option *ngFor="let image of qemuImages" [value]="image">
{{image.filename}}
</mat-option>
</mat-select>
<div *ngIf="newImageSelected">
<input
type="file"
accept=".qcow2"
#file
class="nonvisible"
(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">
<form [formGroup]="fourthStepForm">
<mat-radio-group class="radio-group">
<mat-radio-button class="radio-button" value="1" (click)="setDiskImage('existingImage')" checked>Existing image</mat-radio-button>
<mat-radio-button class="radio-button" value="2" (click)="setDiskImage('newImage')">New image</mat-radio-button>
</mat-radio-group>
<mat-select
*ngIf="!newImageSelected"
placeholder="Disk image (hda)"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="selectedImage">
<mat-option *ngFor="let image of qemuImages" [value]="image">
{{image.filename}}
</mat-option>
</mat-select>
<div *ngIf="newImageSelected">
<input
matInput
type="text"
[(ngModel)]="chosenImage"
placeholder="Please enter name"/>
</mat-form-field>
</div>
type="file"
accept=".qcow2"
#file
class="nonvisible"
(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
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>
</mat-step>
</mat-vertical-stepper>

View File

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

View File

@ -7,6 +7,7 @@ import { VpcsService } from '../../../../services/vpcs.service';
import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
import { ToasterService } from '../../../../services/toaster.service';
import { v4 as uuid } from 'uuid';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
@Component({
@ -23,7 +24,8 @@ export class AddVpcsTemplateComponent implements OnInit {
private serverService: ServerService,
private vpcsService: VpcsService,
private router: Router,
private toasterService: ToasterService
private toasterService: ToasterService,
private templateMocksService: TemplateMocksService
) {}
ngOnInit() {
@ -41,19 +43,15 @@ export class AddVpcsTemplateComponent implements OnInit {
addTemplate() {
if (this.templateName) {
let vpcsTemplate: 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: this.templateName,
symbol: ':/symbols/vpcs_guest.svg',
template_id: uuid(),
template_type: 'vpcs'
};
let vpcsTemplate: VpcsTemplate;
this.templateMocksService.getVpcsTemplate().subscribe((template: VpcsTemplate) => {
vpcsTemplate = template;
});
vpcsTemplate.template_id = uuid(),
vpcsTemplate.name = this.templateName,
this.vpcsService.addTemplate(this.server, vpcsTemplate).subscribe((vpcsTemplate) => {
this.router.navigate(['/server', this.server.id, 'preferences', 'vpcs', 'templates']);
});

View File

@ -35,20 +35,28 @@
placeholder="Symbol">
</mat-form-field>
<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]">
{{category[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<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">
{{type}}
</mat-option>
</mat-select>
</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
</mat-checkbox>
</form>

View File

@ -53,7 +53,7 @@ export class VpcsTemplateDetailsComponent implements OnInit {
}
onSave() {
if( this.inputForm.invalid) {
if (this.inputForm.invalid) {
this.toasterService.error(`Fill all required fields`);
} else {
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);
}
}