From c71cf3c574041797ced0f48afe54df27c0013401 Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Tue, 25 Sep 2018 10:07:52 +0700 Subject: [PATCH 01/10] feat(service-offering): show all service offerings even those which don't fir account resource limitation --- .../custom-service-offering.component.html | 8 ++-- .../custom-service-offering.component.ts | 38 +++++++++++++++---- .../service-offering-dialog.component.html | 4 +- .../service-offering-dialog.component.ts | 20 +++++++++- .../service-offering-list.component.html | 5 ++- .../service-offering-list.component.ts | 7 +++- .../selectors/service-offering.selectors.ts | 16 +------- .../vm-creation-service-offering.container.ts | 3 ++ src/i18n/en.json | 3 ++ src/i18n/ru.json | 3 ++ 10 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.html b/src/app/service-offering/custom-service-offering/custom-service-offering.component.html index a994ada989..f771f4956a 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.html +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.html @@ -8,9 +8,9 @@ <h5>{{ 'SERVICE_OFFERING.CUSTOM_SERVICE_OFFERING.CPU_NUMBER' | translate }}</h5> type="number" name="cpuNumber" [min]="offering.customOfferingRestrictions.cpunumber.min" - [max]="offering.customOfferingRestrictions.cpunumber.max" + [max]="maxCpu" [csMinValue]="offering.customOfferingRestrictions.cpunumber.min" - [csMaxValue]="offering.customOfferingRestrictions.cpunumber.max" + [csMaxValue]="maxCpu" formControlName="cpuNumber" required > @@ -38,9 +38,9 @@ <h5>{{ 'SERVICE_OFFERING.CUSTOM_SERVICE_OFFERING.MEMORY' | translate }}</h5> type="number" name="memory" [min]="offering.customOfferingRestrictions.memory.min" - [max]="offering.customOfferingRestrictions.memory.max" + [max]="maxMemory" [csMinValue]="offering.customOfferingRestrictions.memory.min" - [csMaxValue]="offering.customOfferingRestrictions.memory.max" + [csMaxValue]="maxMemory" formControlName="memory" required > diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts index 1a1efe1e9a..1da7dddb18 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts @@ -1,8 +1,19 @@ import { Component, Inject } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms' +import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms' import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { ComputeOfferingViewModel } from '../../vm/view-models'; +import { Account } from '../../shared/models'; + +function LimitValidator(cpuNumberLimit: number): ValidatorFn { + return function (control: FormControl) { + if (control.value > cpuNumberLimit) { + return { cpuLimitExceeded: true }; + } else { + return null; + } + }; +} @Component({ selector: 'cs-custom-service-offering', @@ -12,13 +23,27 @@ import { ComputeOfferingViewModel } from '../../vm/view-models'; export class CustomServiceOfferingComponent { public offering: ComputeOfferingViewModel; public hardwareForm: FormGroup; + public account: Account; + public maxCpu: number; + public maxMemory: number; constructor( @Inject(MAT_DIALOG_DATA) data, public dialogRef: MatDialogRef<CustomServiceOfferingComponent>, ) { - const { offering } = data; - this.offering = offering; + this.offering = data.offering; + this.account = data.account; + if (this.offering.customOfferingRestrictions.cpunumber.max > this.account.cpuavailable) { + this.maxCpu = this.account.cpuavailable + } else { + this.maxCpu = this.offering.customOfferingRestrictions.cpunumber.max; + } + + if (this.offering.customOfferingRestrictions.memory.max > this.account.memoryavailable) { + this.maxMemory = this.account.memoryavailable; + } else { + this.maxMemory = this.offering.customOfferingRestrictions.memory.max; + } this.createForm(); } @@ -35,11 +60,10 @@ export class CustomServiceOfferingComponent { } private createForm() { - // input text=number provide all other validation for current restrictions this.hardwareForm = new FormGroup({ - cpuNumber: new FormControl(this.offering.cpunumber, Validators.required), - cpuSpeed: new FormControl(this.offering.cpuspeed, Validators.required), - memory: new FormControl(this.offering.memory, Validators.required), + cpuNumber: new FormControl(this.offering.cpunumber, [Validators.required, LimitValidator(this.maxCpu)]), + cpuSpeed: new FormControl(this.offering.cpuspeed, [Validators.required]), + memory: new FormControl(this.offering.memory, [Validators.required, LimitValidator(this.maxMemory)]), }); } } diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html index 622219ed11..4f8e29eb5e 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html @@ -14,9 +14,11 @@ <h3 class="mat-dialog-title"> [classes]="classes" [selectedClasses]="selectedClasses" [query]="query" + [account]="account" [offeringList]="serviceOfferings" [selectedOffering]="serviceOffering" [showFields]="showFields" + [resourcesLimitExceeded]="resourcesLimitExceeded" (selectedOfferingChange)="updateOffering($event)" ></cs-service-offering-list> @@ -40,7 +42,7 @@ <h3 class="mat-dialog-title"> <button mat-button color="primary" - [disabled]="isSubmitButtonDisabled()" + [disabled]="isSubmitButtonDisabled() || resourcesLimitExceeded" (click)="onChange()" > {{ (formMode ? 'COMMON.SELECT' : 'COMMON.CHANGE') | translate }} diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts index cce2b581ff..82976492ff 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; -import { ServiceOfferingClass, ServiceOfferingType } from '../../shared/models'; +import { Account, ServiceOfferingClass, ServiceOfferingType } from '../../shared/models'; import { ComputeOfferingViewModel } from '../../vm/view-models'; import { VirtualMachine } from '../../vm/shared/vm.model'; @@ -24,6 +24,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { @Input() public virtualMachine: VirtualMachine; @Input() public groupings: Array<any>; @Input() public query: string; + @Input() public account: Account; @Input() public isVmRunning: boolean; @Output() public onServiceOfferingChange = new EventEmitter<ComputeOfferingViewModel>(); @Output() public onServiceOfferingUpdate = new EventEmitter<ComputeOfferingViewModel>(); @@ -33,6 +34,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { public serviceOffering: ComputeOfferingViewModel; public loading: boolean; public showFields = false; + public resourcesLimitExceeded = false; public ngOnInit() { this.serviceOffering = this.serviceOfferings.find(_ => _.id === this.serviceOfferingId); @@ -40,6 +42,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { this.viewMode === ServiceOfferingType.fixed ? this.viewModeChange.emit(ServiceOfferingType.custom) : this.viewModeChange.emit(ServiceOfferingType.fixed); } + this.checkLimits(this.serviceOffering); } public ngOnChanges(changes: SimpleChanges) { @@ -52,6 +55,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { public updateOffering(offering: ComputeOfferingViewModel): void { this.serviceOffering = offering; + this.checkLimits(this.serviceOffering); this.onServiceOfferingUpdate.emit(this.serviceOffering); } @@ -99,4 +103,18 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { return isDifferentOfferingId || isSameCustomOfferingWithDifferentParams; } + private checkLimits(offering: ComputeOfferingViewModel) { + let сpusExceeded; + let memoryExceeded; + + if (offering.iscustomized) { + сpusExceeded = this.account.cpuavailable < this.serviceOffering['customOfferingRestrictions'].cpunumber.min; + memoryExceeded = this.account.memoryavailable < this.serviceOffering['customOfferingRestrictions'].memory.min; + } else { + сpusExceeded = this.account.cpuavailable < this.serviceOffering.cpunumber; + memoryExceeded = this.account.memoryavailable < this.serviceOffering.memory; + } + this.resourcesLimitExceeded = memoryExceeded || сpusExceeded; + } + } diff --git a/src/app/service-offering/service-offering-list/service-offering-list.component.html b/src/app/service-offering/service-offering-list/service-offering-list.component.html index 23da526d7e..b0ac793fc6 100644 --- a/src/app/service-offering/service-offering-list/service-offering-list.component.html +++ b/src/app/service-offering/service-offering-list/service-offering-list.component.html @@ -80,10 +80,13 @@ <h5>{{ getDescription(group.soClass) | translate }}</h5> </table> </div> - </ng-container> </div> +<mat-error *ngIf="resourcesLimitExceeded"> + {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} +</mat-error> + <cs-no-results *ngIf="!offeringList?.length && !isLoading"></cs-no-results> diff --git a/src/app/service-offering/service-offering-list/service-offering-list.component.ts b/src/app/service-offering/service-offering-list/service-offering-list.component.ts index 369026e3d9..9deb311aec 100644 --- a/src/app/service-offering/service-offering-list/service-offering-list.component.ts +++ b/src/app/service-offering/service-offering-list/service-offering-list.component.ts @@ -5,7 +5,7 @@ import { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; import { classesFilter } from '../../reducers/service-offerings/redux/service-offerings.reducers'; -import { ServiceOffering, ServiceOfferingClass } from '../../shared/models'; +import { Account, ServiceOffering, ServiceOfferingClass } from '../../shared/models'; import { CustomServiceOfferingComponent } from '../custom-service-offering/custom-service-offering.component'; import { Language } from '../../shared/types'; import { ComputeOfferingViewModel } from '../../vm/view-models'; @@ -23,6 +23,8 @@ export class ServiceOfferingListComponent implements OnChanges { @Input() public selectedOffering: ServiceOffering; @Input() public isLoading = false; @Input() public showFields: boolean; + @Input() public account: Account; + @Input() public resourcesLimitExceeded = false; @Output() public selectedOfferingChange = new EventEmitter<ComputeOfferingViewModel>(); public list: Array<{ soClass: ServiceOfferingClass, items: MatTableDataSource<ComputeOfferingViewModel> }>; @@ -61,7 +63,8 @@ export class ServiceOfferingListComponent implements OnChanges { return this.dialog.open(CustomServiceOfferingComponent, { width: '370px', data: { - offering + offering, + account: this.account } }).afterClosed(); diff --git a/src/app/vm/selectors/service-offering.selectors.ts b/src/app/vm/selectors/service-offering.selectors.ts index 8a4fad9b87..2577dcd8b7 100644 --- a/src/app/vm/selectors/service-offering.selectors.ts +++ b/src/app/vm/selectors/service-offering.selectors.ts @@ -61,21 +61,7 @@ const getAvailableByResourcesSync = ( zone: Zone ) => { const availableInZone = getOfferingsAvailableInZone(serviceOfferings, availability, zone); - - return availableInZone.filter(offering => { - let enoughCpus; - let enoughMemory; - - if (offering.iscustomized) { - enoughCpus = resourceUsage.available.cpus >= offering.customOfferingRestrictions.cpunumber.min; - enoughMemory = resourceUsage.available.memory >= offering.customOfferingRestrictions.memory.min; - } else { - enoughCpus = resourceUsage.available.cpus >= offering.cpunumber; - enoughMemory = resourceUsage.available.memory >= offering.memory; - } - - return enoughCpus && enoughMemory; - }); + return availableInZone; }; export const getAvailableOfferingsForVmCreation = createSelector( diff --git a/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts b/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts index 566c2f19a6..7fd8b5373b 100644 --- a/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts +++ b/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts @@ -11,6 +11,7 @@ import * as fromServiceOfferings from '../../../reducers/service-offerings/redux // tslint:disable-next-line import { ServiceOfferingFromMode } from '../../../service-offering/service-offering-dialog/service-offering-dialog.component'; import { UserTagsActions } from '../../../root-store'; +import * as fromAccounts from '../../../reducers/accounts/redux/accounts.reducers'; @Component({ selector: 'cs-vm-creation-service-offering-container', @@ -23,6 +24,7 @@ import { UserTagsActions } from '../../../root-store'; [selectedClasses]="selectedClasses$ | async" [viewMode]="viewMode$ | async" [query]="query$ | async" + [account]="account$ | async" (onServiceOfferingUpdate)="updateServiceOffering($event)" (onServiceOfferingChange)="changeServiceOffering($event)" (viewModeChange)="onViewModeChange($event)" @@ -37,6 +39,7 @@ export class VmCreationServiceOfferingContainerComponent implements OnInit, Afte readonly query$ = this.store.select(fromServiceOfferings.filterQuery); readonly selectedClasses$ = this.store.select(fromServiceOfferings.filterSelectedClasses); readonly viewMode$ = this.store.select(fromServiceOfferings.filterSelectedViewMode); + readonly account$ = this.store.select(fromAccounts.selectUserAccount); public formMode = ServiceOfferingFromMode.SELECT; diff --git a/src/i18n/en.json b/src/i18n/en.json index 2d42a3125e..8ff3f3df37 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -118,6 +118,9 @@ }, "SNAPSHOT_POLICIES": { "HOURLY_TURN_OFF": "Hourly schedule is turned off" + }, + "COMPUTE_OFFERING": { + "RESOURCE_LIMIT_EXCEEDED": "The service offering cannot be selected because it doesn't fit the account resources available." } }, "NOTIFICATIONS": { diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 83972517e9..6671f64af9 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -118,6 +118,9 @@ }, "SNAPSHOT_POLICIES": { "HOURLY_TURN_OFF": "Почасовое расписание отключено" + }, + "COMPUTE_OFFERING": { + "RESOURCE_LIMIT_EXCEEDED": "Вычислительное предложение не может быть выбрано, потому что больше, чем доступные для аккаунта ресурсы." } }, "NOTIFICATIONS": { From 4bf4a0ffaf416573e4880439affb37e8db822eb3 Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Wed, 26 Sep 2018 13:09:41 +0700 Subject: [PATCH 02/10] custom service offering refactoring --- .../custom-service-offering.component.ts | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts index 1da7dddb18..9855b89674 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms' import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; @@ -7,11 +7,9 @@ import { Account } from '../../shared/models'; function LimitValidator(cpuNumberLimit: number): ValidatorFn { return function (control: FormControl) { - if (control.value > cpuNumberLimit) { - return { cpuLimitExceeded: true }; - } else { - return null; - } + return control.value > cpuNumberLimit + ? { cpuLimitExceeded: true } + : null; }; } @@ -20,7 +18,7 @@ function LimitValidator(cpuNumberLimit: number): ValidatorFn { templateUrl: 'custom-service-offering.component.html', styleUrls: ['custom-service-offering.component.scss'] }) -export class CustomServiceOfferingComponent { +export class CustomServiceOfferingComponent implements OnInit { public offering: ComputeOfferingViewModel; public hardwareForm: FormGroup; public account: Account; @@ -33,17 +31,16 @@ export class CustomServiceOfferingComponent { ) { this.offering = data.offering; this.account = data.account; - if (this.offering.customOfferingRestrictions.cpunumber.max > this.account.cpuavailable) { - this.maxCpu = this.account.cpuavailable - } else { - this.maxCpu = this.offering.customOfferingRestrictions.cpunumber.max; - } + } + + public ngOnInit() { + const cpuFromOffering = this.offering.customOfferingRestrictions.cpunumber.max; + const memoryFromOffering = this.offering.customOfferingRestrictions.memory.max; - if (this.offering.customOfferingRestrictions.memory.max > this.account.memoryavailable) { - this.maxMemory = this.account.memoryavailable; - } else { - this.maxMemory = this.offering.customOfferingRestrictions.memory.max; - } + this.maxCpu = cpuFromOffering > this.account.cpuavailable + ? this.account.cpuavailable : cpuFromOffering; + this.maxMemory = memoryFromOffering > this.account.memoryavailable + ? this.account.memoryavailable : memoryFromOffering; this.createForm(); } From 6c5ddd9b3d8b8bbadcdbc0b3137e5910f418ca31 Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Thu, 27 Sep 2018 08:17:58 +0700 Subject: [PATCH 03/10] updates after review --- .../custom-service-offering.component.ts | 12 ++---------- .../service-offering-dialog.component.html | 8 ++++++-- .../service-offering-dialog.component.ts | 7 ++++--- .../service-offering-list.component.html | 4 ---- .../service-offering-list.component.ts | 1 - .../vm/selectors/service-offering.selectors.ts | 16 ++-------------- 6 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts index 9855b89674..c7c79a23ec 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts @@ -5,14 +5,6 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { ComputeOfferingViewModel } from '../../vm/view-models'; import { Account } from '../../shared/models'; -function LimitValidator(cpuNumberLimit: number): ValidatorFn { - return function (control: FormControl) { - return control.value > cpuNumberLimit - ? { cpuLimitExceeded: true } - : null; - }; -} - @Component({ selector: 'cs-custom-service-offering', templateUrl: 'custom-service-offering.component.html', @@ -58,9 +50,9 @@ export class CustomServiceOfferingComponent implements OnInit { private createForm() { this.hardwareForm = new FormGroup({ - cpuNumber: new FormControl(this.offering.cpunumber, [Validators.required, LimitValidator(this.maxCpu)]), + cpuNumber: new FormControl(this.offering.cpunumber, [Validators.required, Validators.max(this.maxCpu)]), cpuSpeed: new FormControl(this.offering.cpuspeed, [Validators.required]), - memory: new FormControl(this.offering.memory, [Validators.required, LimitValidator(this.maxMemory)]), + memory: new FormControl(this.offering.memory, [Validators.required, Validators.max(this.maxMemory)]), }); } } diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html index 4f8e29eb5e..358d0b4733 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html @@ -18,7 +18,6 @@ <h3 class="mat-dialog-title"> [offeringList]="serviceOfferings" [selectedOffering]="serviceOffering" [showFields]="showFields" - [resourcesLimitExceeded]="resourcesLimitExceeded" (selectedOfferingChange)="updateOffering($event)" ></cs-service-offering-list> @@ -28,6 +27,11 @@ <h3 class="mat-dialog-title"> >{{ "SERVICE_OFFERING.VM_WILL_BE_RESTARTED" | translate }} </div> </div> + + <mat-error *ngIf="resourcesLimitExceeded"> + {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} + </mat-error> + <div class="mat-dialog-actions"> <div> <button mat-button color="primary" (click)="showFields = !showFields"> @@ -42,7 +46,7 @@ <h3 class="mat-dialog-title"> <button mat-button color="primary" - [disabled]="isSubmitButtonDisabled() || resourcesLimitExceeded" + [disabled]="isSubmitButtonDisabled()" (click)="onChange()" > {{ (formMode ? 'COMMON.SELECT' : 'COMMON.CHANGE') | translate }} diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts index 82976492ff..203eecd149 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts @@ -85,7 +85,8 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { || isNoOfferingsInCurrentViewMode || isSelectedOfferingFromDifferentViewMode || isSelectedOfferingDoNotHaveParams - || isSelectedOfferingDifferentFromCurrent; + || isSelectedOfferingDifferentFromCurrent + || this.resourcesLimitExceeded; } private isSelectedOfferingDifferent(): boolean { @@ -108,8 +109,8 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { let memoryExceeded; if (offering.iscustomized) { - сpusExceeded = this.account.cpuavailable < this.serviceOffering['customOfferingRestrictions'].cpunumber.min; - memoryExceeded = this.account.memoryavailable < this.serviceOffering['customOfferingRestrictions'].memory.min; + сpusExceeded = this.account.cpuavailable < this.serviceOffering.customOfferingRestrictions.cpunumber.min; + memoryExceeded = this.account.memoryavailable < this.serviceOffering.customOfferingRestrictions.memory.min; } else { сpusExceeded = this.account.cpuavailable < this.serviceOffering.cpunumber; memoryExceeded = this.account.memoryavailable < this.serviceOffering.memory; diff --git a/src/app/service-offering/service-offering-list/service-offering-list.component.html b/src/app/service-offering/service-offering-list/service-offering-list.component.html index b0ac793fc6..aca65c064d 100644 --- a/src/app/service-offering/service-offering-list/service-offering-list.component.html +++ b/src/app/service-offering/service-offering-list/service-offering-list.component.html @@ -83,10 +83,6 @@ <h5>{{ getDescription(group.soClass) | translate }}</h5> </ng-container> </div> -<mat-error *ngIf="resourcesLimitExceeded"> - {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} -</mat-error> - <cs-no-results *ngIf="!offeringList?.length && !isLoading"></cs-no-results> diff --git a/src/app/service-offering/service-offering-list/service-offering-list.component.ts b/src/app/service-offering/service-offering-list/service-offering-list.component.ts index 9deb311aec..d63e28060e 100644 --- a/src/app/service-offering/service-offering-list/service-offering-list.component.ts +++ b/src/app/service-offering/service-offering-list/service-offering-list.component.ts @@ -24,7 +24,6 @@ export class ServiceOfferingListComponent implements OnChanges { @Input() public isLoading = false; @Input() public showFields: boolean; @Input() public account: Account; - @Input() public resourcesLimitExceeded = false; @Output() public selectedOfferingChange = new EventEmitter<ComputeOfferingViewModel>(); public list: Array<{ soClass: ServiceOfferingClass, items: MatTableDataSource<ComputeOfferingViewModel> }>; diff --git a/src/app/vm/selectors/service-offering.selectors.ts b/src/app/vm/selectors/service-offering.selectors.ts index 2577dcd8b7..fe75f44b39 100644 --- a/src/app/vm/selectors/service-offering.selectors.ts +++ b/src/app/vm/selectors/service-offering.selectors.ts @@ -54,16 +54,6 @@ const getOfferingsAvailableInZone = ( }); }; -const getAvailableByResourcesSync = ( - serviceOfferings: ComputeOfferingViewModel[], - availability: OfferingAvailability, - resourceUsage: ResourceStats, - zone: Zone -) => { - const availableInZone = getOfferingsAvailableInZone(serviceOfferings, availability, zone); - return availableInZone; -}; - export const getAvailableOfferingsForVmCreation = createSelector( getComputeOfferingViewModel, configSelectors.get('offeringAvailability'), @@ -74,8 +64,7 @@ export const getAvailableOfferingsForVmCreation = createSelector( return []; } - const resourceUsage = ResourceStats.fromAccount([user]); - return getAvailableByResourcesSync(serviceOfferings, availability, resourceUsage, zone); + return getOfferingsAvailableInZone(serviceOfferings, availability, zone); } ); @@ -98,8 +87,7 @@ export const getAvailableOfferings = createSelector( return []; } - const resourceUsage = ResourceStats.fromAccount([user]); - const availableOfferings = getAvailableByResourcesSync(serviceOfferings, availability, resourceUsage, zone); + const availableOfferings = getOfferingsAvailableInZone(serviceOfferings, availability, zone); const filterByCompatibilityPolicy = VmCompatibilityPolicy.getFilter(compatibilityPolicy, currentOffering); From 1597f1f0011dad73a2546249985b30be911547a1 Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Thu, 27 Sep 2018 11:25:40 +0700 Subject: [PATCH 04/10] update translations --- src/i18n/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 8736ab6300..2949d8bce6 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -120,7 +120,7 @@ "HOURLY_TURN_OFF": "Почасовое расписание отключено" }, "COMPUTE_OFFERING": { - "RESOURCE_LIMIT_EXCEEDED": "Вычислительное предложение не может быть выбрано, потому что больше, чем доступные для аккаунта ресурсы." + "RESOURCE_LIMIT_EXCEEDED": "Вычислительное предложение не может быть выбрано. Размер выбранного предложения превышает доступные ресурсы аккаунта." } }, "NOTIFICATIONS": { From e5eb58d9dde09077f78b98b7ff1c11693236c6df Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Tue, 2 Oct 2018 08:42:22 +0700 Subject: [PATCH 05/10] after code review --- src/app/app-routing.module.ts | 2 +- src/app/core/services/system-tags.service.ts | 0 src/app/home/home.component.ts | 2 +- .../reducers/vm/redux/vm-creation.effects.ts | 18 +- .../custom-service-offering.component.html | 14 +- .../custom-service-offering.component.scss | 4 + .../custom-service-offering.component.ts | 20 +- .../service-offering-dialog.component.html | 6 +- .../service-offering-dialog.component.scss | 4 + .../service-offering-dialog.component.ts | 32 ++- .../service-offering-list.component.ts | 4 +- .../volume-actions/volume-resize.container.ts | 2 +- src/app/shared/models/account-user.model.ts | 8 +- src/app/shared/models/account.model.ts | 52 ++--- src/app/shared/models/offering.model.ts | 12 +- .../shared/models/service-offering.model.ts | 16 +- .../template-tags/tags.component.html | 9 - .../template/template-tags/tags.component.ts | 0 .../template-tags.component.html | 17 +- .../template-tags/template-tags.component.ts | 2 +- ...mpute-offering-view-model.selector.spec.ts | 188 ++++++++++++++++++ .../compute-offering-view-model.selector.ts | 105 +++++++++- .../compute-offering.view-model.ts | 1 + .../service-offering-selector.component.html | 3 + .../service-offering-selector.component.scss | 4 + .../service-offering-selector.component.ts | 6 +- .../containers/vm-creation.container.ts | 7 +- .../vm/vm-creation/data/vm-creation-state.ts | 5 +- .../vm-creation-service-offering.container.ts | 1 - .../vm/vm-creation/vm-creation.component.html | 2 +- .../vm/vm-creation/vm-creation.component.ts | 9 +- src/app/vm/web-shell/web-shell.service.ts | 0 src/testutils/data/accounts.ts | 73 +++++++ src/testutils/data/compute-offerings.ts | 37 ++++ src/testutils/data/index.ts | 2 + 35 files changed, 536 insertions(+), 131 deletions(-) delete mode 100644 src/app/core/services/system-tags.service.ts delete mode 100644 src/app/template/template-tags/tags.component.html delete mode 100644 src/app/template/template-tags/tags.component.ts create mode 100644 src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts delete mode 100644 src/app/vm/web-shell/web-shell.service.ts create mode 100644 src/testutils/data/accounts.ts create mode 100644 src/testutils/data/compute-offerings.ts create mode 100644 src/testutils/data/index.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e09b69dad4..d8f0a2ca89 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -57,7 +57,7 @@ const routes: Routes = [ path: '**', redirectTo: 'instances' } - ] + ], }, { path: '**', diff --git a/src/app/core/services/system-tags.service.ts b/src/app/core/services/system-tags.service.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 4f3ad53c3f..f8e1ed5932 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -30,7 +30,7 @@ export class HomeComponent extends WithUnsubscribe() implements OnInit { this.auth.loggedIn.pipe( takeUntil(this.unsubscribe$), - filter(isLoggedIn => !!isLoggedIn)) + filter(isLoggedIn => isLoggedIn)) .subscribe(() => { this.store.dispatch(new authActions.LoadUserAccountRequest({ name: this.auth.user.account, diff --git a/src/app/reducers/vm/redux/vm-creation.effects.ts b/src/app/reducers/vm/redux/vm-creation.effects.ts index b1059227c1..126f29b8ba 100644 --- a/src/app/reducers/vm/redux/vm-creation.effects.ts +++ b/src/app/reducers/vm/redux/vm-creation.effects.ts @@ -46,6 +46,7 @@ import * as fromTemplates from '../../templates/redux/template.reducers'; import * as fromVMs from './vm.reducers'; import * as fromVMModule from '../../../vm/selectors'; import { KeyboardLayout } from '../../../shared/types'; +import { ComputeOfferingViewModel } from '../../../vm/view-models'; interface VmCreationParams { affinityGroupNames?: string; @@ -160,11 +161,14 @@ export class VirtualMachineCreationEffects { this.store.pipe(select(fromDiskOfferings.selectAll)), this.store.pipe(select(configSelectors.get('defaultComputeOffering'))) ), - map(( - [action, vmCreationState, zones, templates, serviceOfferings, diskOfferings, defaultComputeOfferings]: [ - vmActions.VmFormUpdate, VmCreationState, Zone[], BaseTemplateModel[], ServiceOffering[], DiskOffering[], - DefaultComputeOffering[] - ]) => { + map(([action, vmCreationState, zones, templates, serviceOfferings, diskOfferings, defaultComputeOfferings]: [ + vmActions.VmFormUpdate, + VmCreationState, Zone[], + BaseTemplateModel[], + ComputeOfferingViewModel[], + DiskOffering[], + DefaultComputeOffering[] + ]) => { if (action.payload.zone) { let updates = {}; @@ -669,10 +673,10 @@ export class VirtualMachineCreationEffects { } private getPreselectedOffering( - offerings: ServiceOffering[], + offerings: ComputeOfferingViewModel[], zone: Zone, defaultComputeOfferingConfiguration: DefaultComputeOffering[] - ): ServiceOffering { + ): ComputeOfferingViewModel { const firstOffering = offerings[0]; const configForCurrentZone = defaultComputeOfferingConfiguration.find(config => config.zoneId === zone.id); if (!configForCurrentZone) { diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.html b/src/app/service-offering/custom-service-offering/custom-service-offering.component.html index f771f4956a..1af5a874fb 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.html +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.html @@ -8,9 +8,9 @@ <h5>{{ 'SERVICE_OFFERING.CUSTOM_SERVICE_OFFERING.CPU_NUMBER' | translate }}</h5> type="number" name="cpuNumber" [min]="offering.customOfferingRestrictions.cpunumber.min" - [max]="maxCpu" + [max]="offering.customOfferingRestrictions.cpunumber.max" [csMinValue]="offering.customOfferingRestrictions.cpunumber.min" - [csMaxValue]="maxCpu" + [csMaxValue]="offering.customOfferingRestrictions.cpunumber.max" formControlName="cpuNumber" required > @@ -38,15 +38,19 @@ <h5>{{ 'SERVICE_OFFERING.CUSTOM_SERVICE_OFFERING.MEMORY' | translate }}</h5> type="number" name="memory" [min]="offering.customOfferingRestrictions.memory.min" - [max]="maxMemory" + [max]="offering.customOfferingRestrictions.memory.max" [csMinValue]="offering.customOfferingRestrictions.memory.min" - [csMaxValue]="maxMemory" + [csMaxValue]="offering.customOfferingRestrictions.memory.max" formControlName="memory" required > </mat-form-field> </div> + <mat-error *ngIf="!offering.isAvailableByResources"> + {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} + </mat-error> + <div class="mat-dialog-actions"> <button mat-button @@ -60,7 +64,7 @@ <h5>{{ 'SERVICE_OFFERING.CUSTOM_SERVICE_OFFERING.MEMORY' | translate }}</h5> mat-button color="primary" type="submit" - [disabled]="!hardwareForm.valid" + [disabled]="!hardwareForm.valid || !offering.isAvailableByResources" > {{ 'COMMON.CONFIRM' | translate }} </button> diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss b/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss index a91094ec88..ad26678bc4 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss @@ -9,3 +9,7 @@ h5 { margin-top: 0; } } + +mat-error { + font-size: 13px !important; +} diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts index c7c79a23ec..504fc1c361 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts @@ -14,8 +14,6 @@ export class CustomServiceOfferingComponent implements OnInit { public offering: ComputeOfferingViewModel; public hardwareForm: FormGroup; public account: Account; - public maxCpu: number; - public maxMemory: number; constructor( @Inject(MAT_DIALOG_DATA) data, @@ -26,14 +24,6 @@ export class CustomServiceOfferingComponent implements OnInit { } public ngOnInit() { - const cpuFromOffering = this.offering.customOfferingRestrictions.cpunumber.max; - const memoryFromOffering = this.offering.customOfferingRestrictions.memory.max; - - this.maxCpu = cpuFromOffering > this.account.cpuavailable - ? this.account.cpuavailable : cpuFromOffering; - this.maxMemory = memoryFromOffering > this.account.memoryavailable - ? this.account.memoryavailable : memoryFromOffering; - this.createForm(); } @@ -49,10 +39,14 @@ export class CustomServiceOfferingComponent implements OnInit { } private createForm() { + // input text=number provide all other validation for current restrictions this.hardwareForm = new FormGroup({ - cpuNumber: new FormControl(this.offering.cpunumber, [Validators.required, Validators.max(this.maxCpu)]), - cpuSpeed: new FormControl(this.offering.cpuspeed, [Validators.required]), - memory: new FormControl(this.offering.memory, [Validators.required, Validators.max(this.maxMemory)]), + cpuNumber: new FormControl( + { value: this.offering.cpunumber, disabled: !this.offering.isAvailableByResources }, Validators.required), + cpuSpeed: new FormControl( + { value: this.offering.cpuspeed, disabled: !this.offering.isAvailableByResources }, Validators.required), + memory: new FormControl( + { value: this.offering.memory, disabled: !this.offering.isAvailableByResources }, Validators.required), }); } } diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html index 358d0b4733..d82fd69456 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html @@ -26,11 +26,11 @@ <h3 class="mat-dialog-title"> *ngIf="showRebootMessage" >{{ "SERVICE_OFFERING.VM_WILL_BE_RESTARTED" | translate }} </div> + <mat-error *ngIf="!serviceOffering.isAvailableByResources && isSelectedOfferingViewMode()"> + {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} + </mat-error> </div> - <mat-error *ngIf="resourcesLimitExceeded"> - {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} - </mat-error> <div class="mat-dialog-actions"> <div> diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss index cde163544d..b231378709 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss @@ -20,3 +20,7 @@ flex-direction: column; text-align: right; } + +mat-error { + font-size: 13px !important; +} diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts index 696919c088..d5554e6229 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts @@ -34,7 +34,6 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { public serviceOffering: ComputeOfferingViewModel; public loading: boolean; public showFields = false; - public resourcesLimitExceeded = false; public ngOnInit() { this.serviceOffering = this.serviceOfferings.find(_ => _.id === this.serviceOfferingId); @@ -42,7 +41,6 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { this.viewMode === ServiceOfferingType.fixed ? this.viewModeChange.emit(ServiceOfferingType.custom) : this.viewModeChange.emit(ServiceOfferingType.fixed); } - this.checkLimits(this.serviceOffering); } public ngOnChanges(changes: SimpleChanges) { @@ -55,7 +53,6 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { public updateOffering(offering: ComputeOfferingViewModel): void { this.serviceOffering = offering; - this.checkLimits(this.serviceOffering); this.onServiceOfferingUpdate.emit(this.serviceOffering); } @@ -73,6 +70,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { public isSubmitButtonDisabled(): boolean { const isOfferingNotSelected = !this.serviceOffering; const isNoOfferingsInCurrentViewMode = !this.serviceOfferings.length; + const isNotEnoughResourcesForCurrentOffering = !this.serviceOffering.isAvailableByResources; const isSelectedOfferingFromDifferentViewMode = this.serviceOffering && this.serviceOffering.iscustomized !== (this.viewMode === ServiceOfferingType.custom); const isSelectedOfferingDoNotHaveParams = this.serviceOffering @@ -86,7 +84,18 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { || isSelectedOfferingFromDifferentViewMode || isSelectedOfferingDoNotHaveParams || isSelectedOfferingDifferentFromCurrent - || this.resourcesLimitExceeded; + || isNotEnoughResourcesForCurrentOffering; + } + + public isSelectedOfferingViewMode(): boolean { + if (this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.custom) { + return true; + } + + if (!this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.fixed) { + return true; + } + return false; } private isSelectedOfferingDifferent(): boolean { @@ -103,19 +112,4 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { return isDifferentOfferingId || isSameCustomOfferingWithDifferentParams; } - - private checkLimits(offering: ComputeOfferingViewModel) { - let сpusExceeded; - let memoryExceeded; - - if (offering.iscustomized) { - сpusExceeded = this.account.cpuavailable < this.serviceOffering.customOfferingRestrictions.cpunumber.min; - memoryExceeded = this.account.memoryavailable < this.serviceOffering.customOfferingRestrictions.memory.min; - } else { - сpusExceeded = this.account.cpuavailable < this.serviceOffering.cpunumber; - memoryExceeded = this.account.memoryavailable < this.serviceOffering.memory; - } - this.resourcesLimitExceeded = memoryExceeded || сpusExceeded; - } - } diff --git a/src/app/service-offering/service-offering-list/service-offering-list.component.ts b/src/app/service-offering/service-offering-list/service-offering-list.component.ts index 358250c4da..18eacf1d3d 100644 --- a/src/app/service-offering/service-offering-list/service-offering-list.component.ts +++ b/src/app/service-offering/service-offering-list/service-offering-list.component.ts @@ -20,7 +20,7 @@ export class ServiceOfferingListComponent implements OnChanges { @Input() public classes: Array<ComputeOfferingClass>; @Input() public selectedClasses: Array<string>; @Input() public query: string; - @Input() public selectedOffering: ServiceOffering; + @Input() public selectedOffering: ComputeOfferingViewModel; @Input() public isLoading = false; @Input() public showFields: boolean; @Input() public account: Account; @@ -102,7 +102,7 @@ export class ServiceOfferingListComponent implements OnChanges { } } - public filterOfferings(list: ServiceOffering[], soClass: ComputeOfferingClass) { + public filterOfferings(list: ComputeOfferingViewModel[], soClass: ComputeOfferingClass) { const classesMap = [soClass].reduce((m, i) => ({ ...m, [i.id]: i }), {}); return list.filter(offering => classesFilter(offering, this.classes, classesMap)); } diff --git a/src/app/shared/actions/volume-actions/volume-resize.container.ts b/src/app/shared/actions/volume-actions/volume-resize.container.ts index 122c358d90..b82616e1af 100644 --- a/src/app/shared/actions/volume-actions/volume-resize.container.ts +++ b/src/app/shared/actions/volume-actions/volume-resize.container.ts @@ -32,7 +32,7 @@ export class VolumeResizeContainerComponent implements OnInit { public volume: Volume; - public maxSize = 2; + public maxSize = '2'; constructor( public authService: AuthService, diff --git a/src/app/shared/models/account-user.model.ts b/src/app/shared/models/account-user.model.ts index 7e1ba82377..7e109b3215 100644 --- a/src/app/shared/models/account-user.model.ts +++ b/src/app/shared/models/account-user.model.ts @@ -8,22 +8,22 @@ export interface AccountUser extends BaseModelInterface { firstname: string; lastname: string; email: string; - password?: string; created: string; state: string; account: string; accounttype: number; - roleid: string; roletype: AccountType; rolename: AccountType; + roleid: string; domain: string; domainid: string; timezone: string; accountid: string; iscallerchilddomain: boolean; isdefault: boolean; - secretkey: string; - apikey: string; + password?: string; + apikey?: string; + secretkey?: string; } export interface ApiKeys { diff --git a/src/app/shared/models/account.model.ts b/src/app/shared/models/account.model.ts index 919f72e2f4..7a0aff6900 100644 --- a/src/app/shared/models/account.model.ts +++ b/src/app/shared/models/account.model.ts @@ -41,55 +41,55 @@ export class AccountData { export interface Account extends BaseModelInterface { accounttype: AccountType; - cpuavailable: number; - cpulimit: number; + cpuavailable: string; + cpulimit: string; cputotal: number; domain: string; - fullDomain: string; domainid: string; id: string; - ipavailable: number; - iplimit: number; + ipavailable: string; + iplimit: string; iptotal: number; isdefault: false; - memoryavailable: number; - memorylimit: number; + memoryavailable: string; + memorylimit: string; memorytotal: number; name: string; - networkavailable: number; - networklimit: number; + networkavailable: string; + networklimit: string; networktotal: number; - primarystorageavailable: number; - primarystoragelimit: number; + primarystorageavailable: string; + primarystoragelimit: string; primarystoragetotal: number; - role: string; roleid: string; rolename: string; roletype: string; - receivedbytes: number; - sentbytes: number; - secondarystorageavailable: number; - secondarystoragelimit: number; + receivedbytes?: number; + sentbytes?: number; + secondarystorageavailable: string; + secondarystoragelimit: string; secondarystoragetotal: number; - snapshotavailable: number; - snapshotlimit: number; + snapshotavailable: string; + snapshotlimit: string; snapshottotal: number; state: string; - templateavailable: number; - templatelimit: number; + templateavailable: string; + templatelimit: string; templatetotal: number; user: Array<AccountUser>; - vmavailable: number; - vmlimit: number; + vmavailable: string; + vmlimit: string; vmrunning: number; vmstopped: number; vmtotal: number; - volumeavailable: number; - volumelimit: number; + volumeavailable: string; + volumelimit: string; volumetotal: number; - vpcavailable: number; - vpclimit: number; + vpcavailable: string; + vpclimit: string; vpctotal: number; + role?: string; + fullDomain?: string; } export const isAdmin = (account: Account) => account.accounttype !== AccountType.User; diff --git a/src/app/shared/models/offering.model.ts b/src/app/shared/models/offering.model.ts index bf29039ca7..e45f020385 100644 --- a/src/app/shared/models/offering.model.ts +++ b/src/app/shared/models/offering.model.ts @@ -9,15 +9,15 @@ export interface Offering extends BaseModelInterface { id: string; name: string; displaytext: string; - diskBytesReadRate: number; - diskBytesWriteRate: number; - diskIopsReadRate: number; - diskIopsWriteRate: number; iscustomized: boolean; - miniops: number; - maxiops: number; storagetype: string; provisioningtype: string; + diskBytesReadRate?: number; + diskBytesWriteRate?: number; + diskIopsReadRate?: number; + diskIopsWriteRate?: number; + miniops?: number; + maxiops?: number; } export const isOfferingLocal = (offering: Offering) => offering.storagetype === StorageTypes.local; diff --git a/src/app/shared/models/service-offering.model.ts b/src/app/shared/models/service-offering.model.ts index 07229c19f1..99c6b3687b 100644 --- a/src/app/shared/models/service-offering.model.ts +++ b/src/app/shared/models/service-offering.model.ts @@ -4,19 +4,19 @@ import { userTagKeys } from '../../tags/tag-keys'; export interface ServiceOffering extends Offering { created: string; - cpunumber: number; - cpuspeed: number; - memory: number; - networkrate: string; offerha: boolean; limitcpuuse: boolean; isvolatile: boolean; issystem: boolean; defaultuse: boolean; - deploymentplanner: string; - domain: string; - hosttags: string; - tags: Array<Tag>; + cpunumber?: number; + cpuspeed?: number; + memory?: number; + tags?: Array<Tag>; + domain?: string; + hosttags?: string; + deploymentplanner?: string; + networkrate?: string; } export const ServiceOfferingType = { diff --git a/src/app/template/template-tags/tags.component.html b/src/app/template/template-tags/tags.component.html deleted file mode 100644 index 363508e192..0000000000 --- a/src/app/template/template-tags/tags.component.html +++ /dev/null @@ -1,9 +0,0 @@ -<div *ngIf="entity"> - <cs-tags-view - [tags]="tags" - [hasPermissions]="hasPermissions" - (onTagAdd)="addTag($event)" - (onTagEdit)="editTag($event)" - (onTagDelete)="deleteTag($event)" - ></cs-tags-view> -</div> diff --git a/src/app/template/template-tags/tags.component.ts b/src/app/template/template-tags/tags.component.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/template/template-tags/template-tags.component.html b/src/app/template/template-tags/template-tags.component.html index 1baf64c5bb..363508e192 100644 --- a/src/app/template/template-tags/template-tags.component.html +++ b/src/app/template/template-tags/template-tags.component.html @@ -1,8 +1,9 @@ -<cs-tags-view - [tags]="tags$ | async" - [canAddTag]="!entity.isfeatured" - [hasPermissions]="hasPermissions" - (onTagAdd)="onTagAdd($event)" - (onTagEdit)="onTagEdit($event)" - (onTagDelete)="onTagDelete($event)" -></cs-tags-view> +<div *ngIf="entity"> + <cs-tags-view + [tags]="tags" + [hasPermissions]="hasPermissions" + (onTagAdd)="addTag($event)" + (onTagEdit)="editTag($event)" + (onTagDelete)="deleteTag($event)" + ></cs-tags-view> +</div> diff --git a/src/app/template/template-tags/template-tags.component.ts b/src/app/template/template-tags/template-tags.component.ts index 38b1cfd21c..11a752e420 100644 --- a/src/app/template/template-tags/template-tags.component.ts +++ b/src/app/template/template-tags/template-tags.component.ts @@ -10,7 +10,7 @@ import { KeyValuePair } from '../../tags/tags-view/tags-view.component'; @Component({ selector: 'cs-template-tags', - templateUrl: 'tags.component.html' + templateUrl: 'template-tags.component.html' }) export class TemplateTagsComponent extends TagsComponent<BaseTemplateModel> { @Input() public entity: BaseTemplateModel; diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts new file mode 100644 index 0000000000..cc26e5db43 --- /dev/null +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts @@ -0,0 +1,188 @@ +import { getComputeOfferingViewModel } from './compute-offering-view-model.selector'; +import { customComputeOffering, fixedComputeOffering } from '../../../../testutils/data'; +import { account } from '../../../../testutils/data/accounts'; +import { nonCustomizableProperties } from '../../../core/config/default-configuration'; +import { ComputeOfferingViewModel } from '../../view-models'; +import { Account } from '../../../shared/models'; +import { CustomComputeOfferingParameters } from '../../../shared/models/config/custom-compute-offering-parameters.interface'; + +fdescribe('ComputeOfferingViewModelSelector', () => { + describe('isAvailableByResources', () => { + it ('should be true in fixed compute offering params which satisfy memory and cpu resources', () => { + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [fixedComputeOffering], + account, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); + }); + + describe('should be false in fixed compute offering params which unsatisfied', () => { + it('memory resources', () => { + const limitedAccount: Account = { ...account, memoryavailable: String(fixedComputeOffering.memory - 10) }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [fixedComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); + + it('cpu resources', () => { + const limitedAccount: Account = { ...account, cpuavailable: String(fixedComputeOffering.cpunumber - 1) }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [fixedComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); + }); + + it('should be true in custom compute offering params which satisfy memory and cpu resources', () => { + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + account, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); + }); + + describe('should be false in custom compute offering params which unsatisfied', () => { + it('memory resources', () => { + const memoryavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.memory - 10); + const limitedAccount: Account = { ...account, memoryavailable }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); + + it('cpu resources', () => { + const cpuavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.cpunumber - 1); + const limitedAccount: Account = { ...account, cpuavailable }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); + }); + }); + + it('must set values within restrictions and resources for custom compute offering', () => { + /** + * Min Value Max Resource + * cpu 2 7 8 5 => Value = MaxRestrictions = 5 + * memory 512 4000 8192 2000 => Value = MaxRestrictions = 2000 + */ + const cpuavailable = '5'; + const memoryavailable = '2000'; + const limitedAccount: Account = { ...account, memoryavailable, cpuavailable }; + + const customComputeOfferingParameters: CustomComputeOfferingParameters[] = [ + { + offeringId: customComputeOffering.id, + cpunumber: { min: 2, max: 8, value: 7 }, + cpuspeed: { min: 1000, max: 3000, value: 1500 }, + memory: { min: 512, max: 8192, value: 4000 } + } + ]; + + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + limitedAccount, + customComputeOfferingParameters, + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.cpunumber).toBe(5); + expect(computeOfferingViewModel.memory).toBe(2000); + expect(computeOfferingViewModel.customOfferingRestrictions.cpunumber.max).toBe(5); + expect(computeOfferingViewModel.customOfferingRestrictions.memory.max).toBe(2000); + }); + + it('must set default values within restrictions and resources for custom compute offering', () => { + /** + * Min Value Max Resource + * cpu 2 7 8 5 => Value = MaxRestrictions = 5 + * memory 512 4000 4000 8000 => Value = MaxRestrictions = 4000 + */ + const cpuavailable = '5'; + const memoryavailable = '8000'; + const limitedAccount: Account = { ...account, memoryavailable, cpuavailable }; + + const customComputeOfferingParameters: CustomComputeOfferingParameters[] = [ + { + offeringId: customComputeOffering.id, + cpunumber: { min: 2, max: 8, value: 7 }, + cpuspeed: { min: 1000, max: 3000, value: 1500 }, + memory: { min: 512, max: 4000, value: 4000 } + } + ]; + + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + limitedAccount, + customComputeOfferingParameters, + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.cpunumber).toBe(5); + expect(computeOfferingViewModel.memory).toBe(4000); + expect(computeOfferingViewModel.customOfferingRestrictions.cpunumber.max).toBe(5); + expect(computeOfferingViewModel.customOfferingRestrictions.memory.max).toBe(4000); + }); + + + // it('isAvailableByResources in custom compute offering params which satisfy resource should be true', () => { + // const customComputeOfferingParameters = [ + // { + // 'offeringId': '36de12ed-17f1-441f-903f-ab274832c318', + // 'cpunumber': { + // 'min': 2, + // 'max': 8, + // 'value': 4 + // }, + // 'cpuspeed': { + // 'min': 1000, + // 'max': 3000, + // 'value': 1500 + // }, + // 'memory': { + // 'min': 512, + // 'max': 8192, + // 'value': 512 + // } + // } + // ]; + // + // const computeOfferingViewModel = { ...fixedComputeOffering, isAvailableByResources: true }; + // getComputeOfferingViewModel.projector( + // [fixedComputeOffering], account, customComputeOfferingParameters, null, null, [] + // ).toEqual([computeOfferingViewModel]) + // }) +}); diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts index aeb9630ae9..6f3cdadf56 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts @@ -10,6 +10,12 @@ import { import { ComputeOfferingViewModel } from '../../view-models'; import { configSelectors, UserTagsSelectors } from '../../../root-store'; import * as computeOffering from '../../../reducers/service-offerings/redux/service-offerings.reducers'; +import * as fromAuth from '../../../reducers/auth/redux/auth.reducers'; + +interface Resources { + cpuNumber: number | string; + memory: number | string; +} const getFixedAndCustomOfferingsArrays = (offerings: ServiceOffering[]) => { const offeringsArrays = { @@ -71,7 +77,7 @@ const getCustomHardwareRestrictions = ( }; const getHardwareValuesFromTags = ( - serviceOffering: ComputeOfferingViewModel, + serviceOffering: ServiceOffering, tags: Tag[] ): CustomComputeOfferingHardwareValues | null => { const getValue = (param) => { @@ -90,24 +96,83 @@ const getHardwareValuesFromTags = ( return null; }; +const checkAvailabilityForFixedByResources = ( + cpuNumber: number, + memory: number, + availableResources: Resources +): boolean => { + const isEnoughCpuNumber = availableResources.cpuNumber === 'Unlimited' || cpuNumber <= availableResources.cpuNumber; + const isEnoughMemory = availableResources.memory === 'Unlimited' || memory <= availableResources.memory; + return isEnoughCpuNumber && isEnoughMemory; +}; + +const checkAvailabilityForCustomByResources = ( + cpuNumberRestrictions: HardwareLimits, + memoryRestrictions: HardwareLimits, + availableResources: Resources +): boolean => { + const isEnoughCpuNumber = cpuNumberRestrictions.min <= availableResources.cpuNumber; + const isEnoughMemory = memoryRestrictions.min <= availableResources.memory; + return isEnoughCpuNumber && isEnoughMemory; +}; + const getValueThatSatisfiesRestrictions = (defaultValue: number, restrictions: HardwareLimits) => { if (restrictions.min > defaultValue) { return restrictions.min; - } else if (defaultValue > restrictions.max) { + } + if (defaultValue > restrictions.max) { return restrictions.max; } return defaultValue; }; +const getValueThatSatisfiesResources = (defaultValue: number, resourceLimit: string | number): number => { + const limit = Number(resourceLimit); + if (!isNaN(limit) && limit < defaultValue) { + return limit; + } + + return defaultValue; +}; + +const getRestrictionsThatSatisfiesResources = ( + restrictions: CustomComputeOfferingHardwareRestrictions, + resources: Resources +): CustomComputeOfferingHardwareRestrictions => { + const cpuResource = Number(resources.cpuNumber); + const memoryResource = Number(resources.memory); + let maxCpuNumber = restrictions.cpunumber.max; + if (!isNaN(cpuResource)) { + maxCpuNumber = restrictions.cpunumber.max > cpuResource ? cpuResource : restrictions.cpunumber.max; + } + let maxMemory = restrictions.memory.max; + if (!isNaN(memoryResource)) { + maxMemory = restrictions.memory.max > memoryResource ? memoryResource : restrictions.memory.max; + } + return <CustomComputeOfferingHardwareRestrictions>{ + ...restrictions, + cpunumber: { + min: restrictions.cpunumber.min, + max: maxCpuNumber + }, + memory: { + min: restrictions.memory.min, + max: maxMemory + } + }; +}; + export const getComputeOfferingViewModel = createSelector( computeOffering.selectAll, + fromAuth.getUserAccount, configSelectors.get('customComputeOfferingParameters'), configSelectors.get('defaultCustomComputeOfferingRestrictions'), configSelectors.get('customComputeOfferingHardwareValues'), UserTagsSelectors.getServiceOfferingParamTags, ( offerings, + account, customComputeOfferingParameters, defaultRestrictions, defaultHardwareValues, @@ -125,24 +190,52 @@ export const getComputeOfferingViewModel = createSelector( const prioritizedHardwareValues = hardwareValuesFromTags || customHardwareValues || defaultHardwareValues; const prioritizedRestrictions = customHardwareRestrictions || defaultRestrictions; - const cpunumber = getValueThatSatisfiesRestrictions( + const availableResources: Resources = { + cpuNumber: account && account.cpuavailable || 'Infinity', + memory: account && account.memoryavailable || 'Infinity' + }; + const isAvailableByResources = checkAvailabilityForCustomByResources( + prioritizedRestrictions.cpunumber, prioritizedRestrictions.memory, availableResources); + + let cpunumber = getValueThatSatisfiesRestrictions( prioritizedHardwareValues.cpunumber, prioritizedRestrictions.cpunumber); const cpuspeed = getValueThatSatisfiesRestrictions( prioritizedHardwareValues.cpuspeed, prioritizedRestrictions.cpuspeed); - const memory = getValueThatSatisfiesRestrictions( + let memory = getValueThatSatisfiesRestrictions( prioritizedHardwareValues.memory, prioritizedRestrictions.memory); + if (isAvailableByResources) { + cpunumber = getValueThatSatisfiesResources(cpunumber, availableResources.cpuNumber); + memory = getValueThatSatisfiesResources(memory, availableResources.memory); + } + + const customOfferingRestrictions = getRestrictionsThatSatisfiesResources( + prioritizedRestrictions, availableResources); const offeringViewModel: ComputeOfferingViewModel = { ...offering, cpunumber, cpuspeed, memory, - customOfferingRestrictions: prioritizedRestrictions + customOfferingRestrictions, + isAvailableByResources }; return offeringViewModel; }); - return [...fixedOfferings, ...customOfferingsWithMetadata]; + const fixedOfferingWithMeta = fixedOfferings.map(offering => { + const availableResources: Resources = { + cpuNumber: account && account.cpuavailable || 'Infinity', + memory: account && account.memoryavailable || 'Infinity' + }; + const offeringViewModel: ComputeOfferingViewModel = { + ...offering, + isAvailableByResources: checkAvailabilityForFixedByResources( + offering.cpunumber, offering.memory, availableResources) + }; + return offeringViewModel; + }); + + return [...fixedOfferingWithMeta, ...customOfferingsWithMetadata]; } ); diff --git a/src/app/vm/view-models/compute-offering.view-model.ts b/src/app/vm/view-models/compute-offering.view-model.ts index 329cf697bf..a9122b5eac 100644 --- a/src/app/vm/view-models/compute-offering.view-model.ts +++ b/src/app/vm/view-models/compute-offering.view-model.ts @@ -1,5 +1,6 @@ import { CustomComputeOfferingHardwareRestrictions, ServiceOffering } from '../../shared/models'; export interface ComputeOfferingViewModel extends ServiceOffering { + isAvailableByResources: boolean; customOfferingRestrictions?: CustomComputeOfferingHardwareRestrictions; } diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html index 784a5c83d8..a68569e594 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html @@ -14,6 +14,9 @@ <h4 class="dialog-select-header"> <span class="service-offering-info truncate" [matTooltip]="offeringName | async"> {{ offeringName | async }} </span> + <mat-error *ngIf="!serviceOffering.isAvailableByResources"> + {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} + </mat-error> </ng-template> <ng-template #nameStub> {{ 'VM_PAGE.VM_CREATION.NO_OFFERINGS' | translate }} diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss index 113be9a32a..042905a17d 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss @@ -18,3 +18,7 @@ text-align: left; padding-top: 18px; } + +mat-error { + font-size: 10pt !important; +} diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts index cb28bbaf85..8b70d8bffa 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts @@ -19,7 +19,7 @@ export class ServiceOfferingSelectorComponent { @Input() public serviceOfferings: Array<ComputeOfferingViewModel>; @Output() public change: EventEmitter<ServiceOffering>; - private _serviceOffering: ServiceOffering; + private _serviceOffering: ComputeOfferingViewModel; constructor( private dialog: MatDialog, @@ -29,11 +29,11 @@ export class ServiceOfferingSelectorComponent { } @Input() - public get serviceOffering(): ServiceOffering { + public get serviceOffering(): ComputeOfferingViewModel { return this._serviceOffering; } - public set serviceOffering(serviceOffering: ServiceOffering) { + public set serviceOffering(serviceOffering: ComputeOfferingViewModel) { this._serviceOffering = serviceOffering; } diff --git a/src/app/vm/vm-creation/containers/vm-creation.container.ts b/src/app/vm/vm-creation/containers/vm-creation.container.ts index 0cec90d411..b626e0dc08 100644 --- a/src/app/vm/vm-creation/containers/vm-creation.container.ts +++ b/src/app/vm/vm-creation/containers/vm-creation.container.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { MatDialogRef } from '@angular/material'; import { select, Store } from '@ngrx/store'; import { combineLatest, Observable } from 'rxjs'; -import { first, map } from 'rxjs/operators'; +import { first, filter, map } from 'rxjs/operators'; import { AccountResourceType, @@ -38,6 +38,7 @@ import * as fromVMs from '../../../reducers/vm/redux/vm.reducers'; import * as zoneActions from '../../../reducers/zones/redux/zones.actions'; import * as fromZones from '../../../reducers/zones/redux/zones.reducers'; import { getAvailableOfferingsForVmCreation } from '../../selectors'; +import { ComputeOfferingViewModel } from '../../view-models'; @Component({ selector: 'cs-vm-creation-container', @@ -127,14 +128,14 @@ export class VmCreationContainerComponent implements OnInit { } public ngOnInit() { - this.store.dispatch(new vmActions.VmCreationFormInit()); + this.store.dispatch(new vmActions.VmCreationFormInit()) } public onDisplayNameChange(displayName: string) { this.store.dispatch(new vmActions.VmFormUpdate({ displayName })); } - public onServiceOfferingChange(serviceOffering: ServiceOffering) { + public onServiceOfferingChange(serviceOffering: ComputeOfferingViewModel) { this.store.dispatch(new vmActions.VmFormUpdate({ serviceOffering })); } diff --git a/src/app/vm/vm-creation/data/vm-creation-state.ts b/src/app/vm/vm-creation/data/vm-creation-state.ts index aa237295dd..6869bb285f 100644 --- a/src/app/vm/vm-creation/data/vm-creation-state.ts +++ b/src/app/vm/vm-creation/data/vm-creation-state.ts @@ -1,6 +1,7 @@ -import { AffinityGroup, DiskOffering, InstanceGroup, ServiceOffering, SSHKeyPair, Zone } from '../../../shared/models'; +import { AffinityGroup, DiskOffering, InstanceGroup, SSHKeyPair, Zone } from '../../../shared/models'; import { BaseTemplateModel } from '../../../template/shared'; import { VmCreationSecurityGroupData } from '../security-group/vm-creation-security-group-data'; +import { ComputeOfferingViewModel } from '../../view-models'; export interface NotSelected { name: string; @@ -17,7 +18,7 @@ export interface VmCreationState { rootDiskSize: number; rootDiskMinSize: number; securityGroupData: VmCreationSecurityGroupData; - serviceOffering: ServiceOffering; + serviceOffering: ComputeOfferingViewModel; sshKeyPair: SSHKeyPair | NotSelected; template: BaseTemplateModel; zone: Zone; diff --git a/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts b/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts index 0ae0887038..87e2c2577c 100644 --- a/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts +++ b/src/app/vm/vm-creation/service-offering/vm-creation-service-offering.container.ts @@ -10,7 +10,6 @@ import * as fromServiceOfferings from '../../../reducers/service-offerings/redux // tslint:disable-next-line import { ServiceOfferingFromMode } from '../../../service-offering/service-offering-dialog/service-offering-dialog.component'; import * as fromAccounts from '../../../reducers/accounts/redux/accounts.reducers'; -import { UserTagsActions } from '../../../root-store'; @Component({ selector: 'cs-vm-creation-service-offering-container', diff --git a/src/app/vm/vm-creation/vm-creation.component.html b/src/app/vm/vm-creation/vm-creation.component.html index 2fcb8a1ac3..be4cbcb0c5 100644 --- a/src/app/vm/vm-creation/vm-creation.component.html +++ b/src/app/vm/vm-creation/vm-creation.component.html @@ -234,7 +234,7 @@ <h4>{{ 'VM_PAGE.VM_CREATION.SSH_KEY_PAIR' | translate }}</h4> mat-button color="primary" type="submit" - [disabled]="!vmCreateForm.valid || nameIsTaken || !vmCreationState.template" + [disabled]="isSubmitButtonDisabled(vmCreateForm.valid)" > {{ 'COMMON.CREATE' | translate }} </button> diff --git a/src/app/vm/vm-creation/vm-creation.component.ts b/src/app/vm/vm-creation/vm-creation.component.ts index 0f3520f52f..2da91c3b6a 100644 --- a/src/app/vm/vm-creation/vm-creation.component.ts +++ b/src/app/vm/vm-creation/vm-creation.component.ts @@ -95,7 +95,7 @@ export class VmCreationComponent { && !!this.vmCreationState.rootDiskMinSize; } - public get rootDiskSizeLimit(): number { + public get rootDiskSizeLimit(): string { return this.account && this.account.primarystorageavailable; } @@ -150,4 +150,11 @@ export class VmCreationComponent { e.preventDefault(); this.deploy.emit(this.vmCreationState); } + + public isSubmitButtonDisabled(isFormValid: boolean): boolean { + return !isFormValid + || this.nameIsTaken + || !this.vmCreationState.template + || !this.vmCreationState.serviceOffering.isAvailableByResources; + } } diff --git a/src/app/vm/web-shell/web-shell.service.ts b/src/app/vm/web-shell/web-shell.service.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/testutils/data/accounts.ts b/src/testutils/data/accounts.ts new file mode 100644 index 0000000000..8e20942bb3 --- /dev/null +++ b/src/testutils/data/accounts.ts @@ -0,0 +1,73 @@ +import { Account } from '../../app/shared/models'; +import { AccountUser } from '../../app/shared/models/account-user.model'; +import { AccountType } from '../../app/shared/models/account.model'; + +const user: AccountUser = { + id: 'de80acbc-5984-48fd-90dd-061c5c3f9ba9', + username: 'test', + firstname: 'firstname', + lastname: 'lastname', + email: 'hey@bk.ru', + created: '2018-09-24T04:58:53+0000', + state: 'enabled', + account: 'test', + accounttype: 0, + roleid: 'ff636770-acbe-11e8-b088-0242ac110002', + roletype: AccountType.User, + rolename: AccountType.User, + domainid: 'ef2a7031-acbe-11e8-b088-0242ac110002', + domain: 'ROOT', + timezone: 'Etc/GMT 12', + accountid: '22dd3d42-37c7-42fe-bb60-cbf9ec805538', + iscallerchilddomain: false, + isdefault: false, +}; + +export const account: Account = { + id: '22dd3d42-37c7-42fe-bb60-cbf9ec805538', + name: 'test', + accounttype: AccountType.User, + roleid: 'ff636770-acbe-11e8-b088-0242ac110002', + roletype: 'User', + rolename: 'User', + domainid: 'ef2a7031-acbe-11e8-b088-0242ac110002', + domain: 'ROOT', + vmlimit: '20', + vmtotal: 0, + vmavailable: '20', + iplimit: '20', + iptotal: 0, + ipavailable: '0', + volumelimit: '20', + volumetotal: 0, + volumeavailable: '20', + snapshotlimit: '20', + snapshottotal: 0, + snapshotavailable: '20', + templatelimit: '20', + templatetotal: 0, + templateavailable: '20', + vmstopped: 0, + vmrunning: 0, + networklimit: '20', + networktotal: 0, + networkavailable: '20', + vpclimit: '20', + vpctotal: 0, + vpcavailable: '20', + cpulimit: '5', + cputotal: 0, + cpuavailable: 'Infinity', + memorylimit: '40000', + memorytotal: 0, + memoryavailable: 'Infinity', + primarystoragelimit: '1000', + primarystoragetotal: 0, + primarystorageavailable: '1000', + secondarystoragelimit: '400', + secondarystoragetotal: 0, + secondarystorageavailable: '400.0', + state: 'enabled', + user: [user], + isdefault: false, +}; diff --git a/src/testutils/data/compute-offerings.ts b/src/testutils/data/compute-offerings.ts new file mode 100644 index 0000000000..6da80a105c --- /dev/null +++ b/src/testutils/data/compute-offerings.ts @@ -0,0 +1,37 @@ +import { ServiceOffering } from '../../app/shared/models'; + +export const fixedComputeOffering: ServiceOffering = { + id: '36de12ed-17f1-441f-903f-ab274832c318', + name: 'Medium Instance', + displaytext: 'Medium Instance', + cpunumber: 1, + cpuspeed: 1000, + memory: 1024, + created: '2018-08-31T01:50:04+0000', + storagetype: 'shared', + provisioningtype: 'thin', + offerha: false, + limitcpuuse: false, + isvolatile: false, + issystem: false, + defaultuse: false, + iscustomized: false, +}; + + +export const customComputeOffering: ServiceOffering = { + id: '9f55af11-99de-40b7-ab36-45c576296766', + name: 'custom', + displaytext: 'any', + created: '2018-08-31T01:55:05+0000', + storagetype: 'shared', + provisioningtype: 'thin', + offerha: false, + limitcpuuse: false, + isvolatile: false, + domain: 'ROOT', + issystem: false, + defaultuse: false, + iscustomized: true, +}; + diff --git a/src/testutils/data/index.ts b/src/testutils/data/index.ts new file mode 100644 index 0000000000..3551aaccec --- /dev/null +++ b/src/testutils/data/index.ts @@ -0,0 +1,2 @@ +export * from './compute-offerings'; +export * from './vitrual-machines'; From 5345a259ac4e105b2115e37efbd9fe39aefc24f1 Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Tue, 2 Oct 2018 09:09:00 +0700 Subject: [PATCH 06/10] replace mat error by span, and fix test --- .../custom-service-offering.component.html | 4 ++-- .../custom-service-offering.component.scss | 3 ++- .../service-offering-dialog.component.html | 4 ++-- .../service-offering-dialog.component.scss | 3 ++- .../compute-offering-view-model.selector.spec.ts | 6 ++++-- .../compute-offering-view-model.selector.ts | 8 ++++---- .../service-offering-selector.component.html | 4 ++-- .../service-offering-selector.component.scss | 3 ++- .../service-offering-selector.component.ts | 2 +- .../model-services/fixtures/serviceOfferings.json | 12 ++++++++---- 10 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.html b/src/app/service-offering/custom-service-offering/custom-service-offering.component.html index 1af5a874fb..5c11cfee5d 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.html +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.html @@ -47,9 +47,9 @@ <h5>{{ 'SERVICE_OFFERING.CUSTOM_SERVICE_OFFERING.MEMORY' | translate }}</h5> </mat-form-field> </div> - <mat-error *ngIf="!offering.isAvailableByResources"> + <span class="error-message" *ngIf="!offering.isAvailableByResources"> {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} - </mat-error> + </span> <div class="mat-dialog-actions"> <button diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss b/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss index ad26678bc4..06c02ce713 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss @@ -10,6 +10,7 @@ h5 { } } -mat-error { +.error-message { + color: red; font-size: 13px !important; } diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html index d82fd69456..481afb2929 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html @@ -26,9 +26,9 @@ <h3 class="mat-dialog-title"> *ngIf="showRebootMessage" >{{ "SERVICE_OFFERING.VM_WILL_BE_RESTARTED" | translate }} </div> - <mat-error *ngIf="!serviceOffering.isAvailableByResources && isSelectedOfferingViewMode()"> + <span class="error-message" *ngIf="!serviceOffering.isAvailableByResources && isSelectedOfferingViewMode()"> {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} - </mat-error> + </span> </div> diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss index b231378709..2f012f4ea6 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss @@ -21,6 +21,7 @@ text-align: right; } -mat-error { +.error-message { + color: red; font-size: 13px !important; } diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts index cc26e5db43..e39d2a6356 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts @@ -4,9 +4,11 @@ import { account } from '../../../../testutils/data/accounts'; import { nonCustomizableProperties } from '../../../core/config/default-configuration'; import { ComputeOfferingViewModel } from '../../view-models'; import { Account } from '../../../shared/models'; -import { CustomComputeOfferingParameters } from '../../../shared/models/config/custom-compute-offering-parameters.interface'; +import { + CustomComputeOfferingParameters +} from '../../../shared/models/config/custom-compute-offering-parameters.interface'; -fdescribe('ComputeOfferingViewModelSelector', () => { +describe('ComputeOfferingViewModelSelector', () => { describe('isAvailableByResources', () => { it ('should be true in fixed compute offering params which satisfy memory and cpu resources', () => { const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts index 6f3cdadf56..943cd813ff 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts @@ -191,8 +191,8 @@ export const getComputeOfferingViewModel = createSelector( const prioritizedRestrictions = customHardwareRestrictions || defaultRestrictions; const availableResources: Resources = { - cpuNumber: account && account.cpuavailable || 'Infinity', - memory: account && account.memoryavailable || 'Infinity' + cpuNumber: account && account.cpuavailable || '0', + memory: account && account.memoryavailable || '0' }; const isAvailableByResources = checkAvailabilityForCustomByResources( prioritizedRestrictions.cpunumber, prioritizedRestrictions.memory, availableResources); @@ -225,8 +225,8 @@ export const getComputeOfferingViewModel = createSelector( const fixedOfferingWithMeta = fixedOfferings.map(offering => { const availableResources: Resources = { - cpuNumber: account && account.cpuavailable || 'Infinity', - memory: account && account.memoryavailable || 'Infinity' + cpuNumber: account && account.cpuavailable || '0', + memory: account && account.memoryavailable || '0' }; const offeringViewModel: ComputeOfferingViewModel = { ...offering, diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html index a68569e594..f3247023f6 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.html @@ -14,9 +14,9 @@ <h4 class="dialog-select-header"> <span class="service-offering-info truncate" [matTooltip]="offeringName | async"> {{ offeringName | async }} </span> - <mat-error *ngIf="!serviceOffering.isAvailableByResources"> + <span class="error-message" *ngIf="!serviceOffering.isAvailableByResources"> {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} - </mat-error> + </span> </ng-template> <ng-template #nameStub> {{ 'VM_PAGE.VM_CREATION.NO_OFFERINGS' | translate }} diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss index 042905a17d..0fcba5e346 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss @@ -19,6 +19,7 @@ padding-top: 18px; } -mat-error { +.error-message { + color: red; font-size: 10pt !important; } diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts index 8b70d8bffa..cff8122f7f 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.ts @@ -13,7 +13,7 @@ import { VmCreationServiceOfferingContainerComponent } from '../../service-offer @Component({ selector: 'cs-service-offering-selector', templateUrl: 'service-offering-selector.component.html', - styleUrls: ['service-offering-selector.component.scss'], + styleUrls: ['service-offering-selector.component.scss'] }) export class ServiceOfferingSelectorComponent { @Input() public serviceOfferings: Array<ComputeOfferingViewModel>; diff --git a/src/testutils/mocks/model-services/fixtures/serviceOfferings.json b/src/testutils/mocks/model-services/fixtures/serviceOfferings.json index ccd2f5cd3f..51b81d43af 100644 --- a/src/testutils/mocks/model-services/fixtures/serviceOfferings.json +++ b/src/testutils/mocks/model-services/fixtures/serviceOfferings.json @@ -14,7 +14,8 @@ "isvolatile": false, "issystem": false, "defaultuse": false, - "iscustomized": false + "iscustomized": false, + "isAvailableByResources": true }, { "id": "b1196c0e-0c1a-4416-bea8-f6a62309fac5", @@ -31,7 +32,8 @@ "isvolatile": false, "issystem": false, "defaultuse": false, - "iscustomized": false + "iscustomized": false, + "isAvailableByResources": true }, { "id": "a18d52b6-268e-421c-9d0c-1c1635d3b9b9", @@ -50,7 +52,8 @@ "iscustomized": true, "cpunumber": 2, "cpuspeed": 500, - "memory": 1024 + "memory": 1024, + "isAvailableByResources": true }, { "id": "a18d52b6-268e-421c-9d0c-1c1635d3b9b9", @@ -66,6 +69,7 @@ "domain": "develop", "issystem": false, "defaultuse": false, - "iscustomized": true + "iscustomized": true, + "isAvailableByResources": true } ] From f5fd86ae75ee806aca6b3db25ba17e63c08efaaf Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Tue, 2 Oct 2018 14:28:43 +0700 Subject: [PATCH 07/10] small fixes --- .../custom-service-offering.component.scss | 4 +- .../custom-service-offering.component.ts | 8 +- .../service-offering-dialog.component.scss | 4 +- .../service-offering-dialog.component.ts | 1 - ...mpute-offering-view-model.selector.spec.ts | 162 +++++++----------- .../service-offering-selector.component.scss | 4 +- .../containers/vm-creation.container.ts | 14 +- 7 files changed, 75 insertions(+), 122 deletions(-) diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss b/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss index 06c02ce713..ae72dcc5d0 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.scss @@ -11,6 +11,6 @@ h5 { } .error-message { - color: red; - font-size: 13px !important; + color: #f44336; + font-size: 13px; } diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts index 504fc1c361..6675b6b23d 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms' +import { FormControl, FormGroup, Validators } from '@angular/forms' import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { ComputeOfferingViewModel } from '../../vm/view-models'; @@ -15,10 +15,8 @@ export class CustomServiceOfferingComponent implements OnInit { public hardwareForm: FormGroup; public account: Account; - constructor( - @Inject(MAT_DIALOG_DATA) data, - public dialogRef: MatDialogRef<CustomServiceOfferingComponent>, - ) { + constructor(@Inject(MAT_DIALOG_DATA) data, + public dialogRef: MatDialogRef<CustomServiceOfferingComponent>,) { this.offering = data.offering; this.account = data.account; } diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss index 2f012f4ea6..68a32bebc6 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.scss @@ -22,6 +22,6 @@ } .error-message { - color: red; - font-size: 13px !important; + color: #f44336; + font-size: 13px; } diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts index d5554e6229..a8db072062 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts @@ -91,7 +91,6 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { if (this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.custom) { return true; } - if (!this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.fixed) { return true; } diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts index e39d2a6356..515157eef8 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts @@ -4,13 +4,11 @@ import { account } from '../../../../testutils/data/accounts'; import { nonCustomizableProperties } from '../../../core/config/default-configuration'; import { ComputeOfferingViewModel } from '../../view-models'; import { Account } from '../../../shared/models'; -import { - CustomComputeOfferingParameters -} from '../../../shared/models/config/custom-compute-offering-parameters.interface'; +import { CustomComputeOfferingParameters } from '../../../shared/models/config/custom-compute-offering-parameters.interface'; describe('ComputeOfferingViewModelSelector', () => { - describe('isAvailableByResources', () => { - it ('should be true in fixed compute offering params which satisfy memory and cpu resources', () => { + it('isAvailableByResources should be true in fixed compute offering params which satisfy memory and cpu resources', + () => { const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( [fixedComputeOffering], account, @@ -22,75 +20,70 @@ describe('ComputeOfferingViewModelSelector', () => { expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); }); - describe('should be false in fixed compute offering params which unsatisfied', () => { - it('memory resources', () => { - const limitedAccount: Account = { ...account, memoryavailable: String(fixedComputeOffering.memory - 10) }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [fixedComputeOffering], - limitedAccount, - [], - nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, - nonCustomizableProperties.customComputeOfferingHardwareValues, - [] - ); - expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); - }); + it('should be false in fixed compute offering params which unsatisfied memory resources', () => { + const limitedAccount: Account = { ...account, memoryavailable: String(fixedComputeOffering.memory - 10) }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [fixedComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); - it('cpu resources', () => { - const limitedAccount: Account = { ...account, cpuavailable: String(fixedComputeOffering.cpunumber - 1) }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [fixedComputeOffering], - limitedAccount, - [], - nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, - nonCustomizableProperties.customComputeOfferingHardwareValues, - [] - ); - expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); - }); - }); + it('should be false in fixed compute offering params which unsatisfied cpu resources', () => { + const limitedAccount: Account = { ...account, cpuavailable: String(fixedComputeOffering.cpunumber - 1) }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [fixedComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); - it('should be true in custom compute offering params which satisfy memory and cpu resources', () => { - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], - account, - [], - nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, - nonCustomizableProperties.customComputeOfferingHardwareValues, - [] - ); - expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); - }); + it('should be true in custom compute offering params which satisfy memory and cpu resources', () => { + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + account, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); + }); - describe('should be false in custom compute offering params which unsatisfied', () => { - it('memory resources', () => { - const memoryavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.memory - 10); - const limitedAccount: Account = { ...account, memoryavailable }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], - limitedAccount, - [], - nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, - nonCustomizableProperties.customComputeOfferingHardwareValues, - [] - ); - expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); - }); + it('should be false in custom compute offering params which unsatisfied memory resources', () => { + const memoryavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.memory - 10); + const limitedAccount: Account = { ...account, memoryavailable }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); - it('cpu resources', () => { - const cpuavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.cpunumber - 1); - const limitedAccount: Account = { ...account, cpuavailable }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], - limitedAccount, - [], - nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, - nonCustomizableProperties.customComputeOfferingHardwareValues, - [] - ); - expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); - }); - }); + it('should be false in custom compute offering params which unsatisfied cpu resources', () => { + const cpuavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.cpunumber - 1); + const limitedAccount: Account = { ...account, cpuavailable }; + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( + [customComputeOffering], + limitedAccount, + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [] + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); }); it('must set values within restrictions and resources for custom compute offering', () => { @@ -158,33 +151,4 @@ describe('ComputeOfferingViewModelSelector', () => { expect(computeOfferingViewModel.customOfferingRestrictions.cpunumber.max).toBe(5); expect(computeOfferingViewModel.customOfferingRestrictions.memory.max).toBe(4000); }); - - - // it('isAvailableByResources in custom compute offering params which satisfy resource should be true', () => { - // const customComputeOfferingParameters = [ - // { - // 'offeringId': '36de12ed-17f1-441f-903f-ab274832c318', - // 'cpunumber': { - // 'min': 2, - // 'max': 8, - // 'value': 4 - // }, - // 'cpuspeed': { - // 'min': 1000, - // 'max': 3000, - // 'value': 1500 - // }, - // 'memory': { - // 'min': 512, - // 'max': 8192, - // 'value': 512 - // } - // } - // ]; - // - // const computeOfferingViewModel = { ...fixedComputeOffering, isAvailableByResources: true }; - // getComputeOfferingViewModel.projector( - // [fixedComputeOffering], account, customComputeOfferingParameters, null, null, [] - // ).toEqual([computeOfferingViewModel]) - // }) }); diff --git a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss index 0fcba5e346..e8ed895110 100644 --- a/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss +++ b/src/app/vm/vm-creation/components/service-offering-selector/service-offering-selector.component.scss @@ -20,6 +20,6 @@ } .error-message { - color: red; - font-size: 10pt !important; + color: #f44336; + font-size: 10pt; } diff --git a/src/app/vm/vm-creation/containers/vm-creation.container.ts b/src/app/vm/vm-creation/containers/vm-creation.container.ts index b626e0dc08..5017b111d7 100644 --- a/src/app/vm/vm-creation/containers/vm-creation.container.ts +++ b/src/app/vm/vm-creation/containers/vm-creation.container.ts @@ -2,17 +2,9 @@ import { Component, OnInit } from '@angular/core'; import { MatDialogRef } from '@angular/material'; import { select, Store } from '@ngrx/store'; import { combineLatest, Observable } from 'rxjs'; -import { first, filter, map } from 'rxjs/operators'; - -import { - AccountResourceType, - AffinityGroup, - DiskOffering, - InstanceGroup, - ServiceOffering, - SSHKeyPair, - Zone -} from '../../../shared/models'; +import { first, map } from 'rxjs/operators'; + +import { AccountResourceType, AffinityGroup, DiskOffering, InstanceGroup, SSHKeyPair, Zone } from '../../../shared/models'; import { AuthService } from '../../../shared/services/auth.service'; import { BaseTemplateModel } from '../../../template/shared'; import { VmService } from '../../shared/vm.service'; From 65e57c21a371592ead42bbfbaea27dd7a864209a Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Tue, 2 Oct 2018 14:32:45 +0700 Subject: [PATCH 08/10] lint fixes --- .../custom-service-offering.component.ts | 6 ++++-- .../compute-offering-view-model.selector.spec.ts | 4 +++- .../vm/vm-creation/containers/vm-creation.container.ts | 9 ++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts index 6675b6b23d..8e652e3fb4 100644 --- a/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts +++ b/src/app/service-offering/custom-service-offering/custom-service-offering.component.ts @@ -15,8 +15,10 @@ export class CustomServiceOfferingComponent implements OnInit { public hardwareForm: FormGroup; public account: Account; - constructor(@Inject(MAT_DIALOG_DATA) data, - public dialogRef: MatDialogRef<CustomServiceOfferingComponent>,) { + constructor( + @Inject(MAT_DIALOG_DATA) data, + public dialogRef: MatDialogRef<CustomServiceOfferingComponent> + ) { this.offering = data.offering; this.account = data.account; } diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts index 515157eef8..9caf8f8c2b 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts @@ -4,7 +4,9 @@ import { account } from '../../../../testutils/data/accounts'; import { nonCustomizableProperties } from '../../../core/config/default-configuration'; import { ComputeOfferingViewModel } from '../../view-models'; import { Account } from '../../../shared/models'; -import { CustomComputeOfferingParameters } from '../../../shared/models/config/custom-compute-offering-parameters.interface'; +import { + CustomComputeOfferingParameters +} from '../../../shared/models/config/custom-compute-offering-parameters.interface'; describe('ComputeOfferingViewModelSelector', () => { it('isAvailableByResources should be true in fixed compute offering params which satisfy memory and cpu resources', diff --git a/src/app/vm/vm-creation/containers/vm-creation.container.ts b/src/app/vm/vm-creation/containers/vm-creation.container.ts index 5017b111d7..eb9f6498ba 100644 --- a/src/app/vm/vm-creation/containers/vm-creation.container.ts +++ b/src/app/vm/vm-creation/containers/vm-creation.container.ts @@ -4,7 +4,14 @@ import { select, Store } from '@ngrx/store'; import { combineLatest, Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; -import { AccountResourceType, AffinityGroup, DiskOffering, InstanceGroup, SSHKeyPair, Zone } from '../../../shared/models'; +import { + AccountResourceType, + AffinityGroup, + DiskOffering, + InstanceGroup, + SSHKeyPair, + Zone +} from '../../../shared/models'; import { AuthService } from '../../../shared/services/auth.service'; import { BaseTemplateModel } from '../../../template/shared'; import { VmService } from '../../shared/vm.service'; From edf7a0a98b533a7bc7e66ab82c5dadc08e32ef81 Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Fri, 5 Oct 2018 09:58:09 +0700 Subject: [PATCH 09/10] add usedResources in vm editing for available resources --- .../service-offering-dialog.component.html | 2 +- .../service-offering-dialog.component.ts | 4 +- src/app/shared/models/config/index.ts | 1 + .../selectors/service-offering.selectors.ts | 10 +- ...mpute-offering-view-model.selector.spec.ts | 100 +++++++--- .../compute-offering-view-model.selector.ts | 176 ++++++++++++------ src/app/vm/selectors/view-models/index.ts | 2 +- src/testutils/data/index.ts | 1 + 8 files changed, 205 insertions(+), 91 deletions(-) diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html index 481afb2929..6555a2ff02 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.html @@ -26,7 +26,7 @@ <h3 class="mat-dialog-title"> *ngIf="showRebootMessage" >{{ "SERVICE_OFFERING.VM_WILL_BE_RESTARTED" | translate }} </div> - <span class="error-message" *ngIf="!serviceOffering.isAvailableByResources && isSelectedOfferingViewMode()"> + <span class="error-message" *ngIf="!serviceOffering?.isAvailableByResources && isSelectedOfferingViewMode()"> {{ 'ERRORS.COMPUTE_OFFERING.RESOURCE_LIMIT_EXCEEDED' | translate }} </span> </div> diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts index a8db072062..ae29360939 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts @@ -70,7 +70,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { public isSubmitButtonDisabled(): boolean { const isOfferingNotSelected = !this.serviceOffering; const isNoOfferingsInCurrentViewMode = !this.serviceOfferings.length; - const isNotEnoughResourcesForCurrentOffering = !this.serviceOffering.isAvailableByResources; + const isNotEnoughResourcesForCurrentOffering = this.serviceOffering && !this.serviceOffering.isAvailableByResources; const isSelectedOfferingFromDifferentViewMode = this.serviceOffering && this.serviceOffering.iscustomized !== (this.viewMode === ServiceOfferingType.custom); const isSelectedOfferingDoNotHaveParams = this.serviceOffering @@ -88,7 +88,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { } public isSelectedOfferingViewMode(): boolean { - if (this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.custom) { + if (this.serviceOffering && this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.custom) { return true; } if (!this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.fixed) { diff --git a/src/app/shared/models/config/index.ts b/src/app/shared/models/config/index.ts index c5a2d3909a..a816dbc71d 100644 --- a/src/app/shared/models/config/index.ts +++ b/src/app/shared/models/config/index.ts @@ -9,3 +9,4 @@ export * from './image-group.model'; export * from './service-offering-availability.interface'; export * from './offering-compatibility-policy.interface'; export * from './sidenav-config-element.interface'; +export * from './custom-compute-offering-parameters.interface'; diff --git a/src/app/vm/selectors/service-offering.selectors.ts b/src/app/vm/selectors/service-offering.selectors.ts index 7b1aab4a92..98f36864e2 100644 --- a/src/app/vm/selectors/service-offering.selectors.ts +++ b/src/app/vm/selectors/service-offering.selectors.ts @@ -1,7 +1,6 @@ import { createSelector } from '@ngrx/store'; import { VmCompatibilityPolicy } from '../shared/vm-compatibility-policy'; -import { ResourceStats } from '../../shared/services/resource-usage.service'; import { ComputeOfferingViewModel } from '../view-models'; import { isOfferingLocal } from '../../shared/models/offering.model'; import { @@ -10,7 +9,6 @@ import { ServiceOfferingAvailability } from '../../shared/models/config'; import { ServiceOffering, ServiceOfferingType, Zone } from '../../shared/models'; -import { getComputeOfferingViewModel } from './view-models'; import { configSelectors } from '../../root-store'; import * as fromZones from '../../reducers/zones/redux/zones.reducers'; import * as fromAuths from '../../reducers/auth/redux/auth.reducers'; @@ -21,6 +19,10 @@ import { filterSelectedViewMode, getSelectedOffering, } from '../../reducers/service-offerings/redux/service-offerings.reducers'; +import { + getComputeOfferingForVmCreation, + getComputeOfferingForVmEditing +} from './view-models/compute-offering-view-model.selector'; const isComputeOfferingAvailableInZone = ( offering: ServiceOffering, @@ -51,7 +53,7 @@ const getOfferingsAvailableInZone = ( }; export const getAvailableOfferingsForVmCreation = createSelector( - getComputeOfferingViewModel, + getComputeOfferingForVmCreation, configSelectors.get('serviceOfferingAvailability'), fromVMs.getVMCreationZone, fromAuths.getUserAccount, @@ -65,7 +67,7 @@ export const getAvailableOfferingsForVmCreation = createSelector( ); export const getAvailableOfferings = createSelector( - getComputeOfferingViewModel, + getComputeOfferingForVmEditing, getSelectedOffering, configSelectors.get('serviceOfferingAvailability'), configSelectors.get('offeringCompatibilityPolicy'), diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts index 9caf8f8c2b..433df25fc9 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.spec.ts @@ -1,19 +1,19 @@ -import { getComputeOfferingViewModel } from './compute-offering-view-model.selector'; -import { customComputeOffering, fixedComputeOffering } from '../../../../testutils/data'; -import { account } from '../../../../testutils/data/accounts'; +import { account, customComputeOffering, fixedComputeOffering, vm } from '../../../../testutils/data'; import { nonCustomizableProperties } from '../../../core/config/default-configuration'; import { ComputeOfferingViewModel } from '../../view-models'; import { Account } from '../../../shared/models'; +import { CustomComputeOfferingParameters } from '../../../shared/models/config/index'; import { - CustomComputeOfferingParameters -} from '../../../shared/models/config/custom-compute-offering-parameters.interface'; + getComputeOfferingForVmCreation, + getComputeOfferingForVmEditing +} from './compute-offering-view-model.selector'; -describe('ComputeOfferingViewModelSelector', () => { +describe('GetComputeOfferingForVmCreationSelector', () => { it('isAvailableByResources should be true in fixed compute offering params which satisfy memory and cpu resources', () => { - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [fixedComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( account, + [fixedComputeOffering], [], nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -24,9 +24,9 @@ describe('ComputeOfferingViewModelSelector', () => { it('should be false in fixed compute offering params which unsatisfied memory resources', () => { const limitedAccount: Account = { ...account, memoryavailable: String(fixedComputeOffering.memory - 10) }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [fixedComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( limitedAccount, + [fixedComputeOffering], [], nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -37,9 +37,9 @@ describe('ComputeOfferingViewModelSelector', () => { it('should be false in fixed compute offering params which unsatisfied cpu resources', () => { const limitedAccount: Account = { ...account, cpuavailable: String(fixedComputeOffering.cpunumber - 1) }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [fixedComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( limitedAccount, + [fixedComputeOffering], [], nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -49,9 +49,9 @@ describe('ComputeOfferingViewModelSelector', () => { }); it('should be true in custom compute offering params which satisfy memory and cpu resources', () => { - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( account, + [customComputeOffering], [], nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -63,9 +63,9 @@ describe('ComputeOfferingViewModelSelector', () => { it('should be false in custom compute offering params which unsatisfied memory resources', () => { const memoryavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.memory - 10); const limitedAccount: Account = { ...account, memoryavailable }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( limitedAccount, + [customComputeOffering], [], nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -77,9 +77,9 @@ describe('ComputeOfferingViewModelSelector', () => { it('should be false in custom compute offering params which unsatisfied cpu resources', () => { const cpuavailable = String(nonCustomizableProperties.customComputeOfferingHardwareValues.cpunumber - 1); const limitedAccount: Account = { ...account, cpuavailable }; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( limitedAccount, + [customComputeOffering], [], nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -107,9 +107,9 @@ describe('ComputeOfferingViewModelSelector', () => { } ]; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( limitedAccount, + [customComputeOffering], customComputeOfferingParameters, nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -140,9 +140,9 @@ describe('ComputeOfferingViewModelSelector', () => { } ]; - const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingViewModel.projector( - [customComputeOffering], + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmCreation.projector( limitedAccount, + [customComputeOffering], customComputeOfferingParameters, nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, nonCustomizableProperties.customComputeOfferingHardwareValues, @@ -154,3 +154,59 @@ describe('ComputeOfferingViewModelSelector', () => { expect(computeOfferingViewModel.customOfferingRestrictions.memory.max).toBe(4000); }); }); + +describe('GetComputeOfferingForVmEditingSelector', () => { + it('isAvailableByResources should be true in fixed compute offering params which satisfy memory and cpu resources', + () => { + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmEditing.projector( + account, + [fixedComputeOffering], + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [], + vm + ); + expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); + }); + + it('isAvailableByResources should be true, cause satisfy resources plus used resources in editing vm', + () => { + const cpuavailable = '0'; + const memoryavailable = '512'; + const limitedAccount: Account = { ...account, memoryavailable, cpuavailable }; + const updatedVm = { ...vm, memory: '512', cpuNumber: 1 }; + + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmEditing.projector( + limitedAccount, + [fixedComputeOffering], + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [], + updatedVm + ); + + expect(computeOfferingViewModel.isAvailableByResources).toEqual(true); + }); + + it('isAvailableByResources should be false, cause unsatisfy resources plus used resources in editing vm', + () => { + const cpuavailable = '0'; + const memoryavailable = '0'; + const limitedAccount: Account = { ...account, memoryavailable, cpuavailable }; + const updatedVm = { ...vm, memory: '512', cpuNumber: 1 }; + + const [computeOfferingViewModel]: ComputeOfferingViewModel[] = getComputeOfferingForVmEditing.projector( + limitedAccount, + [fixedComputeOffering], + [], + nonCustomizableProperties.defaultCustomComputeOfferingRestrictions, + nonCustomizableProperties.customComputeOfferingHardwareValues, + [], + updatedVm + ); + + expect(computeOfferingViewModel.isAvailableByResources).toEqual(false); + }); +}); diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts index 943cd813ff..6d4771e0bc 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts @@ -11,6 +11,7 @@ import { ComputeOfferingViewModel } from '../../view-models'; import { configSelectors, UserTagsSelectors } from '../../../root-store'; import * as computeOffering from '../../../reducers/service-offerings/redux/service-offerings.reducers'; import * as fromAuth from '../../../reducers/auth/redux/auth.reducers'; +import * as fromVms from '../../../reducers/vm/redux/vm.reducers'; interface Resources { cpuNumber: number | string; @@ -36,7 +37,7 @@ const getCustomOfferingHardwareParameters = ( offering: ServiceOffering, offeringsParameters: CustomComputeOfferingParameters[] ): CustomComputeOfferingParameters | undefined => { - return offeringsParameters.find(parameters => parameters.offeringId === offering.id) + return offeringsParameters && offeringsParameters.find(parameters => parameters.offeringId === offering.id) }; const getCustomHardwareValues = ( @@ -111,8 +112,10 @@ const checkAvailabilityForCustomByResources = ( memoryRestrictions: HardwareLimits, availableResources: Resources ): boolean => { - const isEnoughCpuNumber = cpuNumberRestrictions.min <= availableResources.cpuNumber; - const isEnoughMemory = memoryRestrictions.min <= availableResources.memory; + const isEnoughCpuNumber = availableResources.cpuNumber === 'Unlimited' + || cpuNumberRestrictions.min <= availableResources.cpuNumber; + const isEnoughMemory = availableResources.memory === 'Unlimited' + || memoryRestrictions.min <= availableResources.memory; return isEnoughCpuNumber && isEnoughMemory; }; @@ -163,71 +166,57 @@ const getRestrictionsThatSatisfiesResources = ( }; }; -export const getComputeOfferingViewModel = createSelector( - computeOffering.selectAll, - fromAuth.getUserAccount, - configSelectors.get('customComputeOfferingParameters'), - configSelectors.get('defaultCustomComputeOfferingRestrictions'), - configSelectors.get('customComputeOfferingHardwareValues'), - UserTagsSelectors.getServiceOfferingParamTags, - ( +const getComputeOfferingViewModel = ( offerings, - account, customComputeOfferingParameters, defaultRestrictions, defaultHardwareValues, - tags + tags, + availableResources ): ComputeOfferingViewModel[] => { - const { customOfferings, fixedOfferings } = getFixedAndCustomOfferingsArrays(offerings); - - const customOfferingsWithMetadata: ComputeOfferingViewModel[] = customOfferings - .map((offering: ServiceOffering) => { - const customParameters = getCustomOfferingHardwareParameters(offering, customComputeOfferingParameters); - const customHardwareValues = getCustomHardwareValues(customParameters); - const customHardwareRestrictions = getCustomHardwareRestrictions(customParameters); - const hardwareValuesFromTags = getHardwareValuesFromTags(offering, tags); - - const prioritizedHardwareValues = hardwareValuesFromTags || customHardwareValues || defaultHardwareValues; - const prioritizedRestrictions = customHardwareRestrictions || defaultRestrictions; - - const availableResources: Resources = { - cpuNumber: account && account.cpuavailable || '0', - memory: account && account.memoryavailable || '0' - }; - const isAvailableByResources = checkAvailabilityForCustomByResources( - prioritizedRestrictions.cpunumber, prioritizedRestrictions.memory, availableResources); - - let cpunumber = getValueThatSatisfiesRestrictions( - prioritizedHardwareValues.cpunumber, prioritizedRestrictions.cpunumber); - const cpuspeed = getValueThatSatisfiesRestrictions( - prioritizedHardwareValues.cpuspeed, prioritizedRestrictions.cpuspeed); - let memory = getValueThatSatisfiesRestrictions( - prioritizedHardwareValues.memory, prioritizedRestrictions.memory); - - if (isAvailableByResources) { - cpunumber = getValueThatSatisfiesResources(cpunumber, availableResources.cpuNumber); - memory = getValueThatSatisfiesResources(memory, availableResources.memory); - } - - const customOfferingRestrictions = getRestrictionsThatSatisfiesResources( - prioritizedRestrictions, availableResources); - - const offeringViewModel: ComputeOfferingViewModel = { - ...offering, - cpunumber, - cpuspeed, - memory, - customOfferingRestrictions, - isAvailableByResources - }; - return offeringViewModel; - }); + const { customOfferings, fixedOfferings } = getFixedAndCustomOfferingsArrays(offerings); - const fixedOfferingWithMeta = fixedOfferings.map(offering => { - const availableResources: Resources = { - cpuNumber: account && account.cpuavailable || '0', - memory: account && account.memoryavailable || '0' + const customOfferingsWithMetadata: ComputeOfferingViewModel[] = customOfferings + .map((offering: ServiceOffering) => { + const customParameters = getCustomOfferingHardwareParameters(offering, customComputeOfferingParameters); + const customHardwareValues = getCustomHardwareValues(customParameters); + const customHardwareRestrictions = getCustomHardwareRestrictions(customParameters); + const hardwareValuesFromTags = getHardwareValuesFromTags(offering, tags); + + const prioritizedHardwareValues = hardwareValuesFromTags || customHardwareValues || defaultHardwareValues; + const prioritizedRestrictions = customHardwareRestrictions || defaultRestrictions; + + + const isAvailableByResources = checkAvailabilityForCustomByResources( + prioritizedRestrictions.cpunumber, prioritizedRestrictions.memory, availableResources); + + let cpunumber = getValueThatSatisfiesRestrictions( + prioritizedHardwareValues.cpunumber, prioritizedRestrictions.cpunumber); + const cpuspeed = getValueThatSatisfiesRestrictions( + prioritizedHardwareValues.cpuspeed, prioritizedRestrictions.cpuspeed); + let memory = getValueThatSatisfiesRestrictions( + prioritizedHardwareValues.memory, prioritizedRestrictions.memory); + + if (isAvailableByResources) { + cpunumber = getValueThatSatisfiesResources(cpunumber, availableResources.cpuNumber); + memory = getValueThatSatisfiesResources(memory, availableResources.memory); + } + + const customOfferingRestrictions = getRestrictionsThatSatisfiesResources( + prioritizedRestrictions, availableResources); + + const offeringViewModel: ComputeOfferingViewModel = { + ...offering, + cpunumber, + cpuspeed, + memory, + customOfferingRestrictions, + isAvailableByResources }; + return offeringViewModel; + }); + + const fixedOfferingWithMeta = fixedOfferings.map(offering => { const offeringViewModel: ComputeOfferingViewModel = { ...offering, isAvailableByResources: checkAvailabilityForFixedByResources( @@ -237,5 +226,70 @@ export const getComputeOfferingViewModel = createSelector( }); return [...fixedOfferingWithMeta, ...customOfferingsWithMetadata]; + }; + +export const getComputeOfferingForVmEditing = createSelector( + fromAuth.getUserAccount, + computeOffering.selectAll, + configSelectors.get('customComputeOfferingParameters'), + configSelectors.get('defaultCustomComputeOfferingRestrictions'), + configSelectors.get('customComputeOfferingHardwareValues'), + UserTagsSelectors.getServiceOfferingParamTags, + fromVms.getSelectedVM, + (account, + offerings, + customComputeOfferingParameters, + defaultRestrictions, + defaultHardwareValues, + tags, vm): ComputeOfferingViewModel[] => { + const memoryUsed = vm.memory; + const cpuNumberUsed = vm.cpuNumber; + + const cpuNumber = account && account.cpuavailable === 'Unlimited' + ? account.cpuavailable + : Number(account.cpuavailable) + cpuNumberUsed; + const memory = account && account.memoryavailable === 'Unlimited' + ? account.memoryavailable + : Number(account.memoryavailable) + memoryUsed; + + const availableResources: Resources = { + cpuNumber: cpuNumber || cpuNumberUsed, + memory: memory || memoryUsed + }; + + return getComputeOfferingViewModel( + offerings, + customComputeOfferingParameters, + defaultRestrictions, + defaultHardwareValues, + tags, + availableResources); + } +); + +export const getComputeOfferingForVmCreation = createSelector( + fromAuth.getUserAccount, + computeOffering.selectAll, + configSelectors.get('customComputeOfferingParameters'), + configSelectors.get('defaultCustomComputeOfferingRestrictions'), + configSelectors.get('customComputeOfferingHardwareValues'), + UserTagsSelectors.getServiceOfferingParamTags, + (account, + offerings, + customComputeOfferingParameters, + defaultRestrictions, + defaultHardwareValues, + tags): ComputeOfferingViewModel[] => { + const availableResources: Resources = { + cpuNumber: account && account.cpuavailable || '0', + memory: account && account.memoryavailable || '0' + }; + return getComputeOfferingViewModel( + offerings, + customComputeOfferingParameters, + defaultRestrictions, + defaultHardwareValues, + tags, + availableResources); } ); diff --git a/src/app/vm/selectors/view-models/index.ts b/src/app/vm/selectors/view-models/index.ts index 65a8ed8a70..c7881ca31d 100644 --- a/src/app/vm/selectors/view-models/index.ts +++ b/src/app/vm/selectors/view-models/index.ts @@ -1 +1 @@ -export { getComputeOfferingViewModel } from './compute-offering-view-model.selector'; +export { getComputeOfferingForVmCreation } from './compute-offering-view-model.selector'; diff --git a/src/testutils/data/index.ts b/src/testutils/data/index.ts index 3551aaccec..2d7277d5dd 100644 --- a/src/testutils/data/index.ts +++ b/src/testutils/data/index.ts @@ -1,2 +1,3 @@ export * from './compute-offerings'; export * from './vitrual-machines'; +export * from './accounts'; From 07788b9822e2399c96e6b1bb41dc9f4b02fa0f8b Mon Sep 17 00:00:00 2001 From: HeyRoach <caloriees@gmail.com> Date: Fri, 5 Oct 2018 15:07:41 +0700 Subject: [PATCH 10/10] small updates after review --- .../service-offering-dialog.component.ts | 2 +- .../volume-actions/volume-resize.container.ts | 2 +- .../compute-offering-view-model.selector.ts | 13 ++++++++----- src/app/vm/selectors/view-models/index.ts | 5 ++++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts index ae29360939..a06ecf7c16 100644 --- a/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts +++ b/src/app/service-offering/service-offering-dialog/service-offering-dialog.component.ts @@ -91,7 +91,7 @@ export class ServiceOfferingDialogComponent implements OnInit, OnChanges { if (this.serviceOffering && this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.custom) { return true; } - if (!this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.fixed) { + if (this.serviceOffering && !this.serviceOffering.iscustomized && this.viewMode === ServiceOfferingType.fixed) { return true; } return false; diff --git a/src/app/shared/actions/volume-actions/volume-resize.container.ts b/src/app/shared/actions/volume-actions/volume-resize.container.ts index 5a7042f465..b82616e1af 100644 --- a/src/app/shared/actions/volume-actions/volume-resize.container.ts +++ b/src/app/shared/actions/volume-actions/volume-resize.container.ts @@ -51,7 +51,7 @@ export class VolumeResizeContainerComponent implements OnInit { take(1), filter(account => !!account)) .subscribe((account: Account) => { - this.maxSize = Number(account.primarystorageavailable); + this.maxSize = account.primarystorageavailable; }); } diff --git a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts index 6d4771e0bc..19b655dee2 100644 --- a/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts +++ b/src/app/vm/selectors/view-models/compute-offering-view-model.selector.ts @@ -241,7 +241,8 @@ export const getComputeOfferingForVmEditing = createSelector( customComputeOfferingParameters, defaultRestrictions, defaultHardwareValues, - tags, vm): ComputeOfferingViewModel[] => { + tags, + vm): ComputeOfferingViewModel[] => { const memoryUsed = vm.memory; const cpuNumberUsed = vm.cpuNumber; @@ -252,10 +253,7 @@ export const getComputeOfferingForVmEditing = createSelector( ? account.memoryavailable : Number(account.memoryavailable) + memoryUsed; - const availableResources: Resources = { - cpuNumber: cpuNumber || cpuNumberUsed, - memory: memory || memoryUsed - }; + const availableResources: Resources = { cpuNumber, memory }; return getComputeOfferingViewModel( offerings, @@ -280,6 +278,11 @@ export const getComputeOfferingForVmCreation = createSelector( defaultRestrictions, defaultHardwareValues, tags): ComputeOfferingViewModel[] => { + + /** + * '0' used to prevent an error when account is not loaded yet + * it happened when you go to vm creation dialog by url + */ const availableResources: Resources = { cpuNumber: account && account.cpuavailable || '0', memory: account && account.memoryavailable || '0' diff --git a/src/app/vm/selectors/view-models/index.ts b/src/app/vm/selectors/view-models/index.ts index c7881ca31d..470eaeb895 100644 --- a/src/app/vm/selectors/view-models/index.ts +++ b/src/app/vm/selectors/view-models/index.ts @@ -1 +1,4 @@ -export { getComputeOfferingForVmCreation } from './compute-offering-view-model.selector'; +export { + getComputeOfferingForVmCreation, + getComputeOfferingForVmEditing +} from './compute-offering-view-model.selector';