Support for creating virtual box templates added

This commit is contained in:
Piotr Pekala 2019-01-30 08:40:32 -08:00
parent 41eb0d882d
commit 23c7e3bc8e
27 changed files with 579 additions and 6 deletions

View File

@ -17,6 +17,10 @@ import { VpcsPreferencesComponent } from './components/preferences/vpcs/vpcs-pre
import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templates/vpcs-templates.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 { VirtualBoxPreferencesComponent } from './components/preferences/virtual-box/virtual-box-preferences/virtual-box-preferences.component';
import { VirtualBoxTemplatesComponent } from './components/preferences/virtual-box/virtual-box-templates/virtual-box-templates.component';
import { VirtualBoxTemplateDetailsComponent } from './components/preferences/virtual-box/virtual-box-template-details/virtual-box-template-details.component';
import { AddVirtualBoxTemplateComponent } from './components/preferences/virtual-box/add-virtual-box-template/add-virtual-box-template.component';
const routes: Routes = [
{
@ -40,7 +44,12 @@ const routes: Routes = [
// { path: 'server/:server_id/preferences/vpcs', component: VpcsPreferencesComponent },
{ path: 'server/:server_id/preferences/vpcs/templates', component: VpcsTemplatesComponent },
{ path: 'server/:server_id/preferences/vpcs/templates/:template_id', component: VpcsTemplateDetailsComponent},
{ path: 'server/:server_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent }
{ path: 'server/:server_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent },
// temporary disabled
// { path: 'server/:server_id/preferences/virtualbox', component: VirtualBoxPreferencesComponent }
{ path: 'server/:server_id/preferences/virtualbox/templates', component: VirtualBoxTemplatesComponent },
{ path: 'server/:server_id/preferences/virtualbox/templates/:template_id', component: VirtualBoxTemplateDetailsComponent },
{ path: 'server/:server_id/preferences/virtualbox/addtemplate', component: AddVirtualBoxTemplateComponent }
]
},
{ path: 'server/:server_id/project/:project_id', component: ProjectMapComponent }

View File

@ -103,6 +103,11 @@ 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';
import { VirtualBoxPreferencesComponent } from './components/preferences/virtual-box/virtual-box-preferences/virtual-box-preferences.component';
import { VirtualBoxTemplatesComponent } from './components/preferences/virtual-box/virtual-box-templates/virtual-box-templates.component';
import { VirtualBoxService } from './services/virtual-box.service';
import { VirtualBoxTemplateDetailsComponent } from './components/preferences/virtual-box/virtual-box-template-details/virtual-box-template-details.component';
import { AddVirtualBoxTemplateComponent } from './components/preferences/virtual-box/add-virtual-box-template/add-virtual-box-template.component';
if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@ -164,7 +169,11 @@ if (environment.production) {
VpcsPreferencesComponent,
VpcsTemplatesComponent,
AddVpcsTemplateComponent,
VpcsTemplateDetailsComponent
VpcsTemplateDetailsComponent,
VirtualBoxPreferencesComponent,
VirtualBoxTemplatesComponent,
VirtualBoxTemplateDetailsComponent,
AddVirtualBoxTemplateComponent
],
imports: [
BrowserModule,
@ -214,7 +223,8 @@ if (environment.production) {
ServerSettingsService,
QemuService,
VpcsService,
TemplateMocksService
TemplateMocksService,
VirtualBoxService
],
entryComponents: [
AddServerDialogComponent,

View File

@ -17,6 +17,11 @@
QEMU
</button>
</mat-list-item>
<mat-list-item>
<button mat-button routerLink="/server/{{serverId}}/preferences/virtualbox/templates">
VirtualBox
</button>
</mat-list-item>
</mat-nav-list>
</div>
</div>

View File

@ -1,7 +1,7 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New QEMU template</h1>
<h1 class="col">New QEMU VM template</h1>
</div>
</div>
<div class="default-content">

View File

@ -312,6 +312,9 @@
Usage
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="row">
<textarea matInput type="text" [(ngModel)]="qemuTemplate.usage"></textarea>
</mat-form-field>
</mat-expansion-panel>
</mat-accordion>
<div class="buttons-bar"><button mat-raised-button color="primary" (click)="onSave()">Save</button></div>

View File

@ -0,0 +1,23 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">New VirtualBox VM template</h1>
</div>
</div>
<div class="default-content" *ngIf="virtualBoxTemplate">
<mat-form-field class="form-field">
<mat-select
placeholder="VM list"
[(ngModel)]="selectedVM"
[ngModelOptions]="{standalone: true}" >
<mat-option *ngFor="let vm of virtualMachines" [value]="vm">
{{vm.vmname}}
</mat-option>
</mat-select>
</mat-form-field><br/>
<mat-checkbox [(ngModel)]="virtualBoxTemplate.linked_clone">
Use as a linked base VM (experimental)
</mat-checkbox>
<div class="buttons-bar"><button mat-raised-button color="primary" (click)="addTemplate()">Add template</button></div>
</div>
</div>

View File

@ -0,0 +1,69 @@
import { Component, OnInit } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, ParamMap, Router, Router, Router, Router, Router, Router } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { switchMap } from 'rxjs/operators';
import { VirtualBoxService } from '../../../../services/virtual-box.service';
import { VirtualBoxVm } from '../../../../models/virtualbox/virtualboxVm';
import { ToasterService } from '../../../../services/toaster.service';
import { TemplateMocksService } from '../../../../services/template-mocks.service';
import { VirtualBoxTemplate } from '../../../../models/templates/virtualbox-template';
import { v4 as uuid } from 'uuid';
@Component({
selector: 'app-add-virtual-box-template',
templateUrl: './add-virtual-box-template.component.html',
styleUrls: ['./add-virtual-box-template.component.scss']
})
export class AddVirtualBoxTemplateComponent implements OnInit {
server: Server;
virtualMachines: VirtualBoxVm[];
selectedVM: VirtualBoxVm;
virtualBoxTemplate: VirtualBoxTemplate;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private virtualBoxService: VirtualBoxService,
private toasterService: ToasterService,
private templateMocksService: TemplateMocksService,
private router: Router
) {}
ngOnInit() {
this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {
const server_id = params.get('server_id');
return this.serverService.get(parseInt(server_id, 10));
})
)
.subscribe((server: Server) => {
this.server = server;
this.virtualBoxService.getVirtualMachines(this.server).subscribe((virtualMachines: VirtualBoxVm[]) => {
this.virtualMachines = virtualMachines;
this.templateMocksService.getVirtualBoxTemplate().subscribe((template: VirtualBoxTemplate) => {
this.virtualBoxTemplate = template;
});
})
});
}
addTemplate() {
if (this.selectedVM) {
this.virtualBoxTemplate.name = this.selectedVM.vmname;
this.virtualBoxTemplate.vmname = this.selectedVM.vmname;
this.virtualBoxTemplate.ram = this.selectedVM.ram;
this.virtualBoxTemplate.template_id = uuid();
this.virtualBoxService.addTemplate(this.server, this.virtualBoxTemplate).subscribe((template: VirtualBoxTemplate) => {
this.router.navigate(['/server', this.server.id, 'preferences', 'virtualbox', 'templates']);
});
} else {
this.toasterService.error(`Fill all required fields`);
}
}
}

View File

@ -0,0 +1,12 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">VirtualBox preferences</h1>
</div>
</div>
<div class="default-content">
<mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="vpcsExecutable" placeholder="Path to VPCS executable"/>
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,42 @@
import { Component, OnInit } from "@angular/core";
import { ServerSettingsService } from '../../../../services/server-settings.service';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Server } from '../../../../models/server';
import { switchMap } from 'rxjs/operators';
import { ServerService } from '../../../../services/server.service';
import { ToasterService } from '../../../../services/toaster.service';
@Component({
selector: 'app-virtual-box-preferences',
templateUrl: './virtual-box-preferences.component.html',
styleUrls: ['./virtual-box-preferences.component.scss']
})
export class VirtualBoxPreferencesComponent implements OnInit {
server: Server;
vboxManagePath: string;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private serverSettingsService: ServerSettingsService,
private toasterService: ToasterService
) {}
ngOnInit() {
this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {
const server_id = params.get('server_id');
return this.serverService.get(parseInt(server_id, 10));
})
)
.subscribe((server: Server) => {
this.server = server;
});
}
restoreDefaults(){
this.vboxManagePath = '';
}
}

View File

@ -0,0 +1,140 @@
<div class="content" [ngClass]="{ shadowed: isConfiguratorOpened }" [ngClass]="{ nonshadowed: !isConfiguratorOpened }">
<div class="default-header">
<div class="row">
<h1 class="col">VirtualBox VM configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="virtualBoxTemplate">
<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)]="virtualBoxTemplate.name" placeholder="Template name">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="virtualBoxTemplate.default_name_format" placeholder="Default name format">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="virtualBoxTemplate.symbol" placeholder="Symbol">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Category" [(ngModel)]="virtualBoxTemplate.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" [(ngModel)]="virtualBoxTemplate.console_type">
<mat-option *ngFor="let type of consoleTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="virtualBoxTemplate.console_auto_start">
Auto start console
</mat-checkbox>
<mat-form-field class="row">
<input matInput type="number" [(ngModel)]="virtualBoxTemplate.ram" placeholder="RAM">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="On close" [(ngModel)]="virtualBoxTemplate.on_close">
<mat-option *ngFor="let option of onCloseOptions" [value]="option[1]">
{{option[0]}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [(ngModel)]="virtualBoxTemplate.headless">
Start VM in headless mode
</mat-checkbox><br/>
<mat-checkbox [(ngModel)]="virtualBoxTemplate.linked_clone">
Use as a linked base VM (experimental)
</mat-checkbox>
</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)]="virtualBoxTemplate.adapters" placeholder="Adapters">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="virtualBoxTemplate.first_port_name" placeholder="First port name">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="text" [(ngModel)]="virtualBoxTemplate.port_name_format" placeholder="Name format">
</mat-form-field>
<mat-form-field class="row">
<input matInput type="number" [(ngModel)]="virtualBoxTemplate.port_segment_size" placeholder="Segment size">
</mat-form-field>
<mat-form-field class="row">
<mat-select placeholder="Type" [(ngModel)]="virtualBoxTemplate.adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-raised-button class="configButton" (click)="configureCustomAdapters()">Configure custom adapters</button><br/>
<mat-checkbox [(ngModel)]="virtualBoxTemplate.use_any_adapter">
Allow GNS3 to use any configured VirtualBox adapter
</mat-checkbox>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Usage
</mat-panel-title>
</mat-expansion-panel-header>
<mat-form-field class="row">
<textarea matInput type="text" [(ngModel)]="virtualBoxTemplate.usage"></textarea>
</mat-form-field>
</mat-expansion-panel>
</mat-accordion>
<div class="buttons-bar"><button mat-raised-button color="primary" (click)="onSave()">Save</button></div>
</div>
</div>
<div class="content" class="configurator" *ngIf="isConfiguratorOpened">
<div class="default-header">
<div class="row">
<h1 class="col">Custom adapters configuration</h1>
</div>
</div>
<div class="default-content" *ngIf="virtualBoxTemplate">
<div class="container mat-elevation-z8">
<table class="table" mat-table [dataSource]="adapters">
<ng-container matColumnDef="adapter_number">
<th mat-header-cell *matHeaderCellDef> Adapter number </th>
<td mat-cell *matCellDef="let element"> Adapter {{element.adapter_number}} </td>
</ng-container>
<ng-container matColumnDef="port_name">
<th mat-header-cell *matHeaderCellDef> Port name </th>
<td mat-cell *matCellDef="let element"> Ethernet {{element.adapter_number}} </td>
</ng-container>
<ng-container matColumnDef="adapter_type">
<th mat-header-cell *matHeaderCellDef> Adapter type </th>
<td mat-cell *matCellDef="let element; let i = index;">
<mat-select placeholder="Type" [(ngModel)]="adapters[i].adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type">
{{type}}
</mat-option>
</mat-select>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<div class="row">
<button mat-raised-button color="primary" class="configHideButton" (click)="configureCustomAdapters()">Apply</button><br/>
</div>
</div>
</div>

View File

@ -0,0 +1,41 @@
.row {
width: 100%;
margin-left: 0px;
}
.select {
width: 100%;
}
.configButton {
width: 100%;
margin-bottom: 10px;
}
.configHideButton {
margin-left: 80%;
width: 20%;
margin-bottom: 10px;
}
.shadowed {
display: none;
transition: 0.25s;
}
.nonshadowed {
opacity: 0;
transition: 0.25s;
}
th {
border: 0px!important;
}
th.mat-header-cell {
padding-bottom: 15px;
}
td.mat-cell {
padding-top: 15px;
}

View File

@ -0,0 +1,82 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { Server } from '../../../../models/server';
import { ToasterService } from '../../../../services/toaster.service';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { VirtualBoxService } from '../../../../services/virtual-box.service';
import { VirtualBoxTemplate } from '../../../../models/templates/virtualbox-template';
import { CustomAdapter } from '../../../../models/qemu/qemu-custom-adapter';
@Component({
selector: 'app-virtual-box-template-details',
templateUrl: './virtual-box-template-details.component.html',
styleUrls: ['./virtual-box-template-details.component.scss']
})
export class VirtualBoxTemplateDetailsComponent implements OnInit {
server: Server;
virtualBoxTemplate: VirtualBoxTemplate;
consoleTypes: string[] = ['telnet', 'none'];
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"]];
networkTypes = ["PCnet-PCI II (Am79C970A)",
"PCNet-FAST III (Am79C973)",
"Intel PRO/1000 MT Desktop (82540EM)",
"Intel PRO/1000 T Server (82543GC)",
"Intel PRO/1000 MT Server (82545EM)",
"Paravirtualized Network (virtio-net)"];
adapters: CustomAdapter[] = [];
displayedColumns: string[] = ['adapter_number', 'port_name', 'adapter_type'];
isConfiguratorOpened: boolean = false;
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private virtualBoxService: VirtualBoxService,
private toasterService: ToasterService,
private formBuilder: FormBuilder
) {}
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.virtualBoxService.getTemplate(this.server, template_id).subscribe((virtualBoxTemplate: VirtualBoxTemplate) => {
this.virtualBoxTemplate = virtualBoxTemplate;
for(let i=0; i<this.virtualBoxTemplate.adapters; i++){
let adapter = this.virtualBoxTemplate.custom_adapters.find(elem => elem.adapter_number === i);
if (adapter) {
this.adapters.push(adapter);
} else {
this.adapters.push({
adapter_number: i,
adapter_type: this.virtualBoxTemplate.adapter_type
});
}
}
});
});
}
configureCustomAdapters(){
this.isConfiguratorOpened = !this.isConfiguratorOpened;
this.virtualBoxTemplate.custom_adapters = this.adapters;
}
onSave() {
this.virtualBoxService.saveTemplate(this.server, this.virtualBoxTemplate).subscribe((virtualBoxTemplate: VirtualBoxTemplate) => {
this.toasterService.success("Changes saved");
});
}
}

View File

@ -0,0 +1,17 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">VirtualBox VM templates</h1>
<button *ngIf="server" class="top-button" routerLink="/server/{{server.id}}/preferences/virtualbox/addtemplate" mat-raised-button color="primary">Add Virtual Box VM template</button>
</div>
</div>
<div class="default-content">
<div class="container mat-elevation-z8">
<mat-nav-list *ngIf="server">
<mat-list-item *ngFor='let template of virtualBoxTemplates' routerLink="{{template.template_id}}">
{{template.name}}
</mat-list-item>
</mat-nav-list>
</div>
</div>
</div>

View File

@ -0,0 +1,4 @@
.top-button {
height: 36px;
margin-top: 22px
}

View File

@ -0,0 +1,45 @@
import { Component, OnInit } from "@angular/core";
import { Server } from '../../../../models/server';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { ServerService } from '../../../../services/server.service';
import { switchMap } from 'rxjs/operators';
import { VirtualBoxTemplate } from '../../../../models/templates/virtualbox-template';
import { VirtualBoxService } from '../../../../services/virtual-box.service';
@Component({
selector: 'app-virtual-box-templates',
templateUrl: './virtual-box-templates.component.html',
styleUrls: ['./virtual-box-templates.component.scss']
})
export class VirtualBoxTemplatesComponent implements OnInit {
server: Server;
virtualBoxTemplates: VirtualBoxTemplate[] = [];
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private virtualBoxService: VirtualBoxService
) {}
ngOnInit() {
this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {
const server_id = params.get('server_id');
return this.serverService.get(parseInt(server_id, 10));
})
)
.subscribe((server: Server) => {
this.server = server;
this.virtualBoxService.getTemplates(this.server).subscribe((virtualBoxTemplates: VirtualBoxTemplate[]) => {
virtualBoxTemplates.forEach((template) => {
if ((template.template_type === 'virtualbox') && !template.builtin) {
this.virtualBoxTemplates.push(template);
}
});
});
});
}
}

View File

@ -1,4 +1,4 @@
<div class="content" [ngClass]="{ shadowed: isConfiguratorOpened }" [ngClass]="{ nonshadowed: !isConfiguratorOpened }">
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">VPCS device configuration</h1>

View File

@ -1,3 +1,5 @@
import { CustomAdapter } from '../qemu/qemu-custom-adapter';
export interface VirtualBoxTemplate {
adapter_type: string;
adapters: number;
@ -6,7 +8,7 @@ export interface VirtualBoxTemplate {
compute_id: string;
console_auto_start: boolean;
console_type: string;
custom_adapters?: (null)[] | null;
custom_adapters?: CustomAdapter[];
default_name_format: string;
first_port_name: string;
headless: boolean;

View File

@ -0,0 +1,4 @@
export class VirtualBoxVm {
ram: number;
vmname: string;
}

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { QemuTemplate } from '../models/templates/qemu-template';
import { VpcsTemplate } from '../models/templates/vpcs-template';
import { Observable, of } from 'rxjs';
import { VirtualBoxTemplate } from '../models/templates/virtualbox-template';
@Injectable()
export class TemplateMocksService {
@ -71,4 +72,34 @@ export class TemplateMocksService {
return of(template);
}
getVirtualBoxTemplate() : Observable<VirtualBoxTemplate> {
let template: VirtualBoxTemplate = {
adapter_type: 'Intel PRO/1000 MT Desktop (82540EM)',
adapters: 1,
builtin: false,
category: 'guest',
compute_id: 'local',
console_auto_start: false,
console_type: 'none',
custom_adapters: [],
default_name_format: '{name}-{0}',
first_port_name: '',
headless: false,
linked_clone: false,
name: '',
on_close: 'power_off',
port_name_format: 'Ethernet{0}',
port_segment_size: 0,
ram: 0,
symbol: ':/symbols/vbox_guest.svg',
template_id: '',
template_type: 'virtualbox',
usage: '',
use_any_adapter: false,
vmname: ''
}
return of(template);
}
}

View File

@ -0,0 +1,31 @@
import { Injectable } from "@angular/core";
import { HttpServer } from './http-server.service';
import { Server } from '../models/server';
import { Observable } from 'rxjs';
import { VirtualBoxTemplate } from '../models/templates/virtualbox-template';
import { VirtualBoxVm } from '../models/virtualbox/virtualboxVm';
@Injectable()
export class VirtualBoxService {
constructor(private httpServer: HttpServer) {}
getTemplates(server: Server): Observable<VirtualBoxTemplate[]> {
return this.httpServer.get<VirtualBoxTemplate[]>(server, '/templates') as Observable<VirtualBoxTemplate[]>;
}
getTemplate(server: Server, template_id: string): Observable<VirtualBoxTemplate> {
return this.httpServer.get<VirtualBoxTemplate>(server, `/templates/${template_id}`) as Observable<VirtualBoxTemplate>;
}
addTemplate(server: Server, virtualBoxTemplate: VirtualBoxTemplate): Observable<VirtualBoxTemplate> {
return this.httpServer.post<VirtualBoxTemplate>(server, `/templates`, virtualBoxTemplate) as Observable<VirtualBoxTemplate>;
}
saveTemplate(server: Server, virtualBoxTemplate: VirtualBoxTemplate): Observable<VirtualBoxTemplate> {
return this.httpServer.put<VirtualBoxTemplate>(server, `/templates/${virtualBoxTemplate.template_id}`, virtualBoxTemplate) as Observable<VirtualBoxTemplate>;
}
getVirtualMachines(server: Server) : Observable<VirtualBoxVm[]> {
return this.httpServer.get<VirtualBoxVm[]>(server, '/compute/virtualbox/vms') as Observable<VirtualBoxVm[]>;
}
}