Component for editing Qemu preferences added

This commit is contained in:
Piotr Pekala 2019-01-25 05:11:17 -08:00
parent daab31abb5
commit a813516dac
10 changed files with 557 additions and 18 deletions

View File

@ -9,7 +9,7 @@
<mat-vertical-stepper [linear]="true">
<mat-step label="QEMU VM Name">
<mat-form-field class="form-field">
<input matInput type="text" placeholder="Please choose a descriptive name for your new QEMU virtual machine"/>
<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
@ -37,10 +37,32 @@
</mat-form-field>
</mat-step>
<mat-step label="Disk image">
<mat-radio-group>
<mat-radio-button value="1" (click)="setradio('Male')">Existing image</mat-radio-button>
<mat-radio-button value="2" (click)="setradio('Male')">New image</mat-radio-button>
<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">
<input
matInput
type="text"
[(ngModel)]="chosenImage"
placeholder="Please enter name"/>
</mat-form-field>
</div>
<div class="buttons-bar"><button mat-raised-button color="primary" (click)="addTemplate()">Add template</button></div>
</mat-step>
</mat-vertical-stepper>
</div>

View File

@ -1,3 +1,30 @@
.form-field {
width: 100%;
}
.radio-button {
width: 50%;
padding-top: 20px;
padding-bottom: 30px;
}
.radio-group {
margin-bottom: 20px;
}
.buttons-bar {
padding-top: 20px;
}
.nonvisible {
display: none;
}
.file-button {
width: 18%;
}
.file-name-form-field {
padding-left: 2%;
width: 80%;
}

View File

@ -1,10 +1,13 @@
import { Component, OnInit } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { switchMap } from 'rxjs/operators';
import { QemuService } from '../../../../services/qemu.service';
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';
@Component({
@ -14,16 +17,23 @@ import { QemuBinary } from '../../../../models/qemu/qemu-binary';
})
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;
qemuImages: QemuImage[] = [];
selectedImage: QemuImage;
chosenImage: string;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private qemuService: QemuService
private qemuService: QemuService,
private toasterService: ToasterService,
private router: Router
) {}
ngOnInit() {
@ -39,10 +49,34 @@ export class AddQemuVmTemplateComponent implements OnInit {
this.qemuService.getBinaries(server).subscribe((qemuBinaries: QemuBinary[]) => {
this.qemuBinaries = qemuBinaries;
});
this.qemuService.getImages(server).subscribe((qemuImages: QemuImage[]) => {
this.qemuImages = qemuImages;
});
});
}
setradio(msg){
console.log(msg);
setDiskImage(value: string){
this.newImageSelected = value === "newImage";
}
uploadImageFile(event){
this.chosenImage = event.target.files[0].name;
}
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.router.navigate(['/server', this.server.id, 'preferences', 'qemu', 'templates']);
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -33,10 +33,10 @@ export class QemuPreferencesComponent implements OnInit {
})
)
.subscribe((server: Server) => {
this.server = server;
this.serverSettingsService.getSettingsForQemu(this.server).subscribe((settings: QemuSettings) => {
this.settings = settings;
});
this.server = server;
this.serverSettingsService.getSettingsForQemu(this.server).subscribe((settings: QemuSettings) => {
this.settings = settings;
});
});
}
@ -46,9 +46,9 @@ export class QemuPreferencesComponent implements OnInit {
}
this.serverSettingsService.updateSettingsForQemu(this.server, this.settings)
.subscribe((qemuSettings: QemuSettings) => {
this.toasterService.success(`Changes applied`);
});
.subscribe((qemuSettings: QemuSettings) => {
this.toasterService.success(`Changes applied`);
});
}
restoreDefaults(){

View File

@ -0,0 +1,318 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">QEMU VM configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="qemuTemplate">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
General settings
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.name" placeholder="Template name">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.default_name_format" placeholder="Default name format">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.symbol" placeholder="Symbol">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Category" [(ngModel)]="qemuTemplate.category">
<mat-option *ngFor="let category of categories" [value]="category[1]">
{{category[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="row">
<input matInput type="number" [(ngModel)]="qemuTemplate.ram" placeholder="RAM">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="number" [(ngModel)]="qemuTemplate.cpus" placeholder="vCPUs">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Qemu binary" [(ngModel)]="qemuTemplate.qemu_path">
<mat-option *ngFor="let binary of binaries" [value]="binary.path">
{{binary.path}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Boot priority" [(ngModel)]="qemuTemplate.boot_priority">
<mat-option *ngFor="let priority of bootPriorities" [value]="priority[1]">
{{priority[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="On close" [(ngModel)]="qemuTemplate.on_close">
<mat-option *ngFor="let option of onCloseOptions" [value]="option[1]">
{{option[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="select">
<mat-select placeholder="Console type" [(ngModel)]="qemuTemplate.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="qemuTemplate.console_auto_start">
Auto start console
</mat-checkbox>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
HDD
</mat-panel-title>
</mat-expansion-panel-header>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDA (Primary Master)
</mat-card-subtitle>
<mat-card-content>
<mat-form-field class="row">
<input matInput [(ngModel)]="qemuTemplate.hda_disk_image" placeholder="Disk image">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Disk interface" [(ngModel)]="qemuTemplate.hda_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDB (Primary Slave)
</mat-card-subtitle>
<mat-card-content>
<mat-form-field class="row">
<input matInput [(ngModel)]="qemuTemplate.hdb_disk_image" placeholder="Disk image">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Disk interface" [(ngModel)]="qemuTemplate.hdb_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDC (Secondary Master)
</mat-card-subtitle>
<mat-card-content>
<mat-form-field class="row">
<input matInput [(ngModel)]="qemuTemplate.hdc_disk_image" placeholder="Disk image">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Disk interface" [(ngModel)]="qemuTemplate.hdc_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
HDD (Secondary Slave)
</mat-card-subtitle>
<mat-card-content>
<mat-form-field class="row">
<input matInput [(ngModel)]="qemuTemplate.hdd_disk_image" placeholder="Disk image">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Disk interface" [(ngModel)]="qemuTemplate.hdd_disk_interface">
<mat-option *ngFor="let interface of diskInterfaces" [value]="interface">
{{interface}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
CD/DVD
</mat-panel-title>
</mat-expansion-panel-header>
<div>
<input
type="file"
#filecdrom
class="nonvisible"
(change)="uploadCdromImageFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="qemuTemplate.cdrom_image"
placeholder="Image"/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="filecdrom.click()" class="file-button">Browse</button>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Network
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="row">
<input matInput type="number" [(ngModel)]="qemuTemplate.adapters" placeholder="Adapters">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.first_port_name" placeholder="First port name">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.port_name_format" placeholder="Name format">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="number" [(ngModel)]="qemuTemplate.port_segment_size" placeholder="Segment size">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.mac_address" placeholder="Base MAC">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Type" [(ngModel)]="qemuTemplate.adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type[0]">
{{type[1]}} ({{type[0]}})
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="qemuTemplate.legacy_networking">
Use the legacy networking mode
</mat-checkbox>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Advanced
</mat-panel-title>
</mat-expansion-panel-header>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Linux boot specific settings
</mat-card-subtitle>
<mat-card-content>
<div>
<input
type="file"
#fileinitrd
class="nonvisible"
(change)="uploadInitrdFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="qemuTemplate.initrd"
placeholder="Initial RAM disk (initrd)"/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="fileinitrd.click()" class="file-button">Browse</button>
</div>
<div>
<input
type="file"
#filekernelimage
class="nonvisible"
(change)="uploadKernelImageFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="qemuTemplate.kernel_image"
placeholder="Kernel image"/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="filekerenelimage.click()" class="file-button">Browse</button>
</div>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.kernel_command_line" placeholder="Kernel command line">
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Bios
</mat-card-subtitle>
<mat-card-content>
<div>
<input
type="file"
#filebios
class="nonvisible"
(change)="uploadBiosFile($event)"/>
<mat-form-field class="file-name-form-field">
<input
matInput
type="text"
[(ngModel)]="qemuTemplate.bios_image"
placeholder="Bios image"/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="filebios.click()" class="file-button">Browse</button>
</div>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Optimization
</mat-card-subtitle>
<mat-card-content>
<mat-checkbox [(ngModel)]="activateCpuThrottling">
Activate CPU throttling
</mat-checkbox>
<mat-form-field *ngIf="activateCpuThrottling" class="row">
<input matInput type="number" [(ngModel)]="qemuTemplate.cpu_throttling" placeholder="Perecentage of CPU allowed">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Process priority" [(ngModel)]="qemuTemplate.process_priority">
<mat-option *ngFor="let priority of priorities" [value]="priority">
{{priority}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-title></mat-card-title>
<mat-card-subtitle>
Additional settings
</mat-card-subtitle>
<mat-card-content>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="qemuTemplate.options" placeholder="Options">
</mat-form-field>
<mat-checkbox [(ngModel)]="qemuTemplate.linked_clone">
Use as a linked base VM
</mat-checkbox>
</mat-card-content>
</mat-card>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Usage
</mat-panel-title>
</mat-expansion-panel-header>
</mat-expansion-panel>
</mat-accordion>
<div class="buttons-bar"><button mat-raised-button color="primary" (click)="onSave()">Save</button></div>
</div>
</div>

View File

@ -0,0 +1,21 @@
.row {
width: 100%;
margin-left: 0px;
}
.select {
width: 100%;
}
.nonvisible {
display: none;
}
.file-button {
width: 18%;
}
.file-name-form-field {
padding-right: 2%;
width: 80%;
}

View File

@ -1,4 +1,12 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, ParamMap } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { QemuService } from '../../../../services/qemu.service';
import { Server } from '../../../../models/server';
import { QemuTemplate } from '../../../../models/templates/qemu-template';
import { QemuBinary } from '../../../../models/qemu/qemu-binary';
import { ToasterService } from '../../../../services/toaster.service';
@Component({
selector: 'app-qemu-virtual-machine-template-details',
@ -6,7 +14,98 @@ import { Component, OnInit } from "@angular/core";
styleUrls: ['./qemu-vm-template-details.component.scss']
})
export class QemuVmTemplateDetailsComponent implements OnInit {
constructor(){}
server: Server;
qemuTemplate: QemuTemplate;
ngOnInit(){}
consoleTypes: string[] = ['telnet', 'vnc', 'spice', 'spice+agent', 'none'];
diskInterfaces: string[] = ['ide', 'sata', 'scsi', 'sd', 'mtd', 'floppy', 'pflash', 'virtio', 'none'];
networkTypes = [["e1000", "Intel Gigabit Ethernet"],
["i82550", "Intel i82550 Ethernet"],
["i82551", "Intel i82551 Ethernet"],
["i82557a", "Intel i82557A Ethernet"],
["i82557b", "Intel i82557B Ethernet"],
["i82557c", "Intel i82557C Ethernet"],
["i82558a", "Intel i82558A Ethernet"],
["i82558b", "Intel i82558B Ethernet"],
["i82559a", "Intel i82559A Ethernet"],
["i82559b", "Intel i82559B Ethernet"],
["i82559c", "Intel i82559C Ethernet"],
["i82559er", "Intel i82559ER Ethernet"],
["i82562", "Intel i82562 Ethernet"],
["i82801", "Intel i82801 Ethernet"],
["ne2k_pci", "NE2000 Ethernet"],
["pcnet", "AMD PCNet Ethernet"],
["rtl8139", "Realtek 8139 Ethernet"],
["virtio", "Legacy paravirtualized Network I/O"],
["virtio-net-pci", "Paravirtualized Network I/O"],
["vmxnet3", "VMWare Paravirtualized Ethernet v3"]];
bootPriorities = [["HDD", "c"],
["CD/DVD-ROM", "d"],
["Network", "n"],
["HDD or Network", "cn"],
["HDD or CD/DVD-ROM", "cd"]];
onCloseOptions = [["Power off the VM", "power_off"],
["Send the shutdown signal (ACPI)", "shutdown_signal"],
["Save the VM state", "save_vm_state"]];
categories = [["Default", "guest"],
["Routers", "routers"],
["Switches", "switches"],
["End devices", "end_devices"],
["Security devices", "security_devices"]];
priorities = ["realtime",
"very high",
"high",
"normal",
"low",
"very low"];
binaries: QemuBinary[] = [];
activateCpuThrottling: boolean = true;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private qemuService: QemuService,
private toasterService: ToasterService
){}
ngOnInit(){
const server_id = this.route.snapshot.paramMap.get("server_id");
const template_id = this.route.snapshot.paramMap.get("template_id");
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
this.server = server;
this.qemuService.getTemplate(this.server, template_id).subscribe((qemuTemplate: QemuTemplate) => {
this.qemuTemplate = qemuTemplate;
this.qemuService.getBinaries(server).subscribe((qemuBinaries: QemuBinary[]) => {
this.binaries = qemuBinaries;
});
});
});
}
uploadCdromImageFile(event){
this.qemuTemplate.cdrom_image = event.target.files[0].name;
}
uploadInitrdFile(event){
this.qemuTemplate.initrd = event.target.files[0].name;
}
uploadKernelImageFile(event){
this.qemuTemplate.kernel_image = event.target.files[0].name;
}
uploadBiosFile(event){
this.qemuTemplate.bios_image = event.target.files[0].name;
}
onSave(){
if (!this.activateCpuThrottling){
this.qemuTemplate.cpu_throttling = 0;
}
this.qemuService.saveTemplate(this.server, this.qemuTemplate).subscribe((savedTemplate: QemuTemplate) => {
this.toasterService.success("Changes saved")
});
}
}

View File

@ -1,7 +1,6 @@
import { Component, OnInit } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { TemplateService } from '../../../../services/template.service';
import { ServerService } from '../../../../services/server.service';
import { switchMap } from 'rxjs/operators';
import { QemuTemplate } from '../../../../models/templates/qemu-template';

View File

@ -0,0 +1,6 @@
export class QemuImage {
filename: string;
filesize: number;
md5sum: string;
path: string;
}

View File

@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
import { QemuTemplate } from '../models/templates/qemu-template';
import { Server } from '../models/server';
import { QemuBinary } from '../models/qemu/qemu-binary';
import { QemuImage } from '../models/qemu/qemu-image';
@Injectable()
export class QemuService {
@ -13,7 +14,19 @@ export class QemuService {
return this.httpServer.get<QemuTemplate[]>(server, '/templates') as Observable<QemuTemplate[]>;
}
getTemplate(server: Server, template_id: string): Observable<QemuTemplate> {
return this.httpServer.get<QemuTemplate>(server, `/templates/${template_id}`) as Observable<QemuTemplate>;
}
getBinaries(server: Server): Observable<QemuBinary[]> {
return this.httpServer.get<QemuBinary[]>(server, '/computes/local/qemu/binaries') as Observable<QemuBinary[]>;
}
getImages(server: Server): Observable<QemuImage[]> {
return this.httpServer.get<QemuImage[]>(server, '/compute/qemu/images') as Observable<QemuImage[]>;
}
saveTemplate(server: Server, qemuTemplate: QemuTemplate): Observable<QemuTemplate> {
return this.httpServer.put<QemuTemplate>(server, `/templates/${qemuTemplate.template_id}`, qemuTemplate) as Observable<QemuTemplate>;
}
}