Basic implementation of managing snapshots

This commit is contained in:
Piotr Pekala 2019-03-01 05:35:42 -08:00
parent 3cf7492452
commit e73dc45f4c
17 changed files with 213 additions and 47 deletions

View File

@ -50,6 +50,7 @@ import { CopyQemuVmTemplateComponent } from './components/preferences/qemu/copy-
import { CopyIosTemplateComponent } from './components/preferences/dynamips/copy-ios-template/copy-ios-template.component';
import { CopyDockerTemplateComponent } from './components/preferences/docker/copy-docker-template/copy-docker-template.component';
import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/copy-iou-template/copy-iou-template.component';
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
const routes: Routes = [
{
@ -62,6 +63,7 @@ const routes: Routes = [
{ path: 'server/:server_id/projects', component: ProjectsComponent },
{ path: 'settings', component: SettingsComponent },
{ path: 'installed-software', component: InstalledSoftwareComponent },
{ path: 'server/:server_id/project/:project_id/snapshots', component: ListOfSnapshotsComponent },
{ path: 'server/:server_id/preferences', component: PreferencesComponent },
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
{ path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent},
@ -116,7 +118,9 @@ const routes: Routes = [
{ path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent }
]
},
{ path: 'server/:server_id/project/:project_id', component: ProjectMapComponent }
{
path: 'server/:server_id/project/:project_id', component: ProjectMapComponent,
}
];
@NgModule({

View File

@ -68,7 +68,6 @@ import { RavenState } from './common/error-handlers/raven-state-communicator';
import { ServerDiscoveryComponent } from './components/servers/server-discovery/server-discovery.component';
import { ServerDatabase } from './services/server.database';
import { CreateSnapshotDialogComponent } from './components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component';
import { SnapshotsComponent } from './components/snapshots/snapshots.component';
import { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-item/snapshot-menu-item.component';
import { MATERIAL_IMPORTS } from './material.imports';
import { DrawingService } from './services/drawing.service';
@ -165,6 +164,8 @@ import { SearchFilter } from './filters/searchFilter.pipe';
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
import { ServerManagementService } from './services/server-management.service';
import { DeleteActionComponent } from './components/project-map/context-menu/actions/delete-action/delete-action.component';
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
import { DateFilter } from './filters/dateFilter.pipe';
if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@ -183,7 +184,6 @@ if (environment.production) {
AddServerDialogComponent,
CreateSnapshotDialogComponent,
SnapshotMenuItemComponent,
SnapshotsComponent,
ProjectsComponent,
AddBlankProjectDialogComponent,
ImportProjectDialogComponent,
@ -267,7 +267,9 @@ if (environment.production) {
CopyDockerTemplateComponent,
EmptyTemplatesListComponent,
SymbolsMenuComponent,
SearchFilter
SearchFilter,
DateFilter,
ListOfSnapshotsComponent
],
imports: [
BrowserModule,

View File

@ -6,7 +6,6 @@ import { VpcsService } from '../../../../services/vpcs.service';
import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
@Component({
selector: 'app-vpcs-templates',
templateUrl: './vpcs-templates.component.html',

View File

@ -1,6 +1,11 @@
<h1 mat-dialog-title>Create snapshot</h1>
<div class="title-container">
<h1 mat-dialog-title>Create snapshot</h1>
<button mat-button class="top-button" color="accent" (click)="onNoClick()" routerLink="/server/{{server.id}}/project/{{project.project_id}}/snapshots">Go to snapshots</button>
</div>
<div mat-dialog-content>
<mat-form-field> <input matInput tabindex="1" [(ngModel)]="snapshot.name" placeholder="Name" /> </mat-form-field>
<mat-form-field class="name-input">
<input matInput tabindex="1" [(ngModel)]="snapshot.name" placeholder="Name" />
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()" tabindex="-1" color="accent">No Thanks</button>

View File

@ -0,0 +1,21 @@
.title-container {
display: flex;
align-items: baseline;
justify-content: space-between;
}
.name-input {
width: 100%;
}
.top-button {
outline: none;
box-shadow: none !important;
background: transparent !important;
}
button:focus {
outline: 0 !important;
border: 0 !important;
box-shadow: none !important;
}

View File

@ -1,6 +1,9 @@
import { Component, Inject } from '@angular/core';
import { Snapshot } from '../../../models/snapshot';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { Server } from '../../../models/server';
import { Project } from '../../../models/project';
@Component({
selector: 'app-create-snapshot-dialog',
@ -8,12 +11,17 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
styleUrls: ['./create-snapshot-dialog.component.scss']
})
export class CreateSnapshotDialogComponent {
server: Server;
project: Project;
snapshot: Snapshot = new Snapshot();
constructor(
public dialogRef: MatDialogRef<CreateSnapshotDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {}
) {
this.server = data['server'];
this.project = data['project'];
}
onAddClick(): void {
this.dialogRef.close(this.snapshot);

View File

@ -0,0 +1,38 @@
<div class="content">
<div class="default-header">
<div class="row">
<h1 class="col">Snapshots</h1>
</div>
</div>
<div class="default-content">
<div class="listcontainer mat-elevation-z8">
<mat-table #table matSort (matSortChange)= "sortData($event)" [dataSource]="snapshots">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="creationDate">
<mat-header-cell *matHeaderCellDef mat-sort-header> Date </mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.created_at | datefilter}} </mat-cell>
</ng-container>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
<mat-cell *matCellDef="let row" style="text-align: right">
<button mat-icon-button matTooltip="Restore snapshot" (click)="restoreSnapshot(row)">
<mat-icon aria-label="Restore snapshot">restore</mat-icon>
</button>
<button mat-icon-button matTooltip="Delete snapshot" (click)="deleteSnapshot(row)">
<mat-icon aria-label="Delete snapshot">delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</div>
</div>
</div>

View File

@ -0,0 +1,94 @@
import { Component, OnInit } from '@angular/core';
import { SnapshotService } from '../../../services/snapshot.service';
import { ServerService } from '../../../services/server.service';
import { ActivatedRoute } from '@angular/router';
import { Server } from '../../..//models/server';
import { Snapshot } from '../../../models/snapshot';
import { Sort } from '@angular/material';
import { ProgressDialogService } from '../../../common/progress-dialog/progress-dialog.service';
import { ToasterService } from '../../../services/toaster.service';
import { Project } from '../../../models/project';
import { ProgressDialogComponent } from '../../../common/progress-dialog/progress-dialog.component';
@Component({
selector: 'app-list-of-snapshots',
templateUrl: './list-of-snapshots.component.html',
styleUrls: ['./list-of-snapshots.component.scss']
})
export class ListOfSnapshotsComponent implements OnInit {
server: Server;
projectId: string;
snapshots: Snapshot[];
displayedColumns = ['name', 'creationDate', 'actions'];
constructor(
private snapshotService: SnapshotService,
private serverService: ServerService,
private route: ActivatedRoute,
private progressDialogService: ProgressDialogService,
private toaster: ToasterService
) {}
ngOnInit() {
let serverId = this.route.snapshot.paramMap.get("server_id");
this.projectId = this.route.snapshot.paramMap.get("project_id");
this.serverService.get(parseInt(serverId, 10)).then((server: Server) => {
this.server = server;
this.getSnapshots();
});
}
getSnapshots() {
this.snapshotService.list(this.server, this.projectId).subscribe((snapshots: Snapshot[]) => {
this.snapshots = snapshots;
});
}
restoreSnapshot(snapshot: Snapshot) {
const restoring = this.snapshotService.restore(this.server, this.projectId, snapshot.snapshot_id.toString());
const progress = this.progressDialogService.open();
const subscription = restoring.subscribe(
(project: Project) => {
this.toaster.success(`Snapshot ${snapshot.name} has been restored.`);
progress.close();
}
);
progress.afterClosed().subscribe(result => {
if (result === ProgressDialogComponent.CANCELLED) {
subscription.unsubscribe();
}
});
}
deleteSnapshot(snapshot: Snapshot) {
this.snapshotService.delete(this.server, this.projectId, snapshot.snapshot_id.toString()).subscribe(() => {
this.getSnapshots();
this.toaster.success(`Snapshot ${snapshot.name} has been deleted.`);
});
}
sortData(sort: Sort) {
if (!sort.active || sort.direction === '') return;
let snapshots = this.snapshots.slice();
this.snapshots = snapshots.sort((a, b) => {
const isAsc = sort.direction === 'asc';
if (sort.active === 'name') {
return compareNames(a.name, b.name, isAsc);
} else if (sort.active === 'creationDate') {
return compareDates(+a.created_at, +b.created_at, !isAsc);
} else return 0;
});
}
}
function compareDates(a: number, b: number, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
function compareNames(a: string, b: string, isAsc: boolean) {
a = a.toLowerCase();
b = b.toLowerCase();
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

View File

@ -30,7 +30,11 @@ export class SnapshotMenuItemComponent implements OnInit {
public createSnapshotModal() {
const dialogRef = this.dialog.open(CreateSnapshotDialogComponent, {
width: '250px'
width: '450px',
data: {
server: this.server,
project: this.project
}
});
dialogRef.afterClosed().subscribe(snapshot => {

View File

@ -1 +0,0 @@
<p>snapshots works!</p>

View File

@ -1,24 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SnapshotsComponent } from './snapshots.component';
describe('SnapshotsComponent', () => {
let component: SnapshotsComponent;
let fixture: ComponentFixture<SnapshotsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SnapshotsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SnapshotsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,12 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-snapshots',
templateUrl: './snapshots.component.html',
styleUrls: ['./snapshots.component.scss']
})
export class SnapshotsComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

View File

@ -0,0 +1,21 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'datefilter'
})
export class DateFilter implements PipeTransform {
transform(timestamp: string): string {
let date = new Date(+timestamp*1000);
let hours = date.getHours();
let minutes = "0" + date.getMinutes();
let seconds = "0" + date.getSeconds();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2) + ' ' + day + '/' + month + '/' + year;
}
}

View File

@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import { Server } from '../models/server';
import { Observable } from 'rxjs';
import { HttpServer } from './http-server.service';
import { Snapshot } from '../models/snapshot';
@ -12,7 +11,15 @@ export class SnapshotService {
return this.httpServer.post<Snapshot>(server, `/projects/${project_id}/snapshots`, snapshot);
}
delete(server: Server, project_id: string, snapshot_id: string) {
return this.httpServer.delete(server, `/projects/${project_id}/snapshots/${snapshot_id}`);
}
list(server: Server, project_id: string) {
return this.httpServer.get<Snapshot[]>(server, `/projects/${project_id}/snapshots`);
}
restore(server: Server, project_id: string, snapshot_id: string) {
return this.httpServer.post(server, `/projects/${project_id}/snapshots/${snapshot_id}/restore`, {});
}
}