implement asTotalLimitFor(...) validation for SLA-Infrastructure and SLA-Platform
This commit is contained in:
parent
de9f5b617f
commit
988f7dc23b
@ -59,9 +59,9 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte
|
|||||||
final var totalValue = ofNullable(bookingItem.getSubBookingItems()).orElse(emptyList())
|
final var totalValue = ofNullable(bookingItem.getSubBookingItems()).orElse(emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
.map(subItem -> propDef.getValue(subItem.getResources()))
|
.map(subItem -> propDef.getValue(subItem.getResources()))
|
||||||
.map(HsBookingItemEntityValidator::toNonNullInteger)
|
.map(HsBookingItemEntityValidator::toIntegerWithDefault0)
|
||||||
.reduce(0, Integer::sum);
|
.reduce(0, Integer::sum);
|
||||||
final var maxValue = getNonNullIntegerValue(propDef, bookingItem.getResources());
|
final var maxValue = getIntegerValueWithDefault0(propDef, bookingItem.getResources());
|
||||||
if (propDef.thresholdPercentage() != null ) {
|
if (propDef.thresholdPercentage() != null ) {
|
||||||
return totalValue > (maxValue * propDef.thresholdPercentage() / 100)
|
return totalValue > (maxValue * propDef.thresholdPercentage() / 100)
|
||||||
? "%s' maximum total is %d%s, but actual total %s %d%s, which exceeds threshold of %d%%"
|
? "%s' maximum total is %d%s, but actual total %s %d%s, which exceeds threshold of %d%%"
|
||||||
|
@ -10,13 +10,17 @@ class HsCloudServerBookingItemValidator extends HsBookingItemEntityValidator {
|
|||||||
|
|
||||||
HsCloudServerBookingItemValidator() {
|
HsCloudServerBookingItemValidator() {
|
||||||
super(
|
super(
|
||||||
|
// @formatter:off
|
||||||
booleanProperty("active") .withDefault(true),
|
booleanProperty("active") .withDefault(true),
|
||||||
|
|
||||||
integerProperty("CPUs") .min( 1) .max( 32) .required(),
|
integerProperty("CPUs") .min( 1) .max( 32) .required(),
|
||||||
integerProperty("RAM").unit("GB") .min( 1) .max( 128) .required(),
|
integerProperty("RAM").unit("GB") .min( 1) .max( 128) .required(),
|
||||||
integerProperty("SSD").unit("GB") .min( 25) .max( 1000) .step(25).required(),
|
integerProperty("SSD").unit("GB") .min( 25) .max( 1000) .step(25).required(),
|
||||||
integerProperty("HDD").unit("GB") .min( 0) .max( 4000) .step(250).withDefault(0),
|
integerProperty("HDD").unit("GB") .min( 0) .max( 4000) .step(250).withDefault(0),
|
||||||
integerProperty("Traffic").unit("GB") .min(250) .max(10000) .step(250).required(),
|
integerProperty("Traffic").unit("GB") .min(250) .max(10000) .step(250).required(),
|
||||||
|
|
||||||
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional()
|
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional()
|
||||||
|
// @formatter:on
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,40 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||||
|
|
||||||
class HsPrivateCloudBookingItemValidator extends HsBookingItemEntityValidator {
|
class HsPrivateCloudBookingItemValidator extends HsBookingItemEntityValidator {
|
||||||
|
|
||||||
HsPrivateCloudBookingItemValidator() {
|
HsPrivateCloudBookingItemValidator() {
|
||||||
super(
|
super(
|
||||||
|
// @formatter:off
|
||||||
integerProperty("CPUs") .min( 1).max( 128).required().asTotalLimit(),
|
integerProperty("CPUs") .min( 1).max( 128).required().asTotalLimit(),
|
||||||
integerProperty("RAM").unit("GB") .min( 1).max( 512).required().asTotalLimit(),
|
integerProperty("RAM").unit("GB") .min( 1).max( 512).required().asTotalLimit(),
|
||||||
integerProperty("SSD").unit("GB") .min( 25).max( 4000).step(25).required().asTotalLimit(),
|
integerProperty("SSD").unit("GB") .min( 25).max( 4000).step(25).required().asTotalLimit(),
|
||||||
integerProperty("HDD").unit("GB") .min( 0).max(16000).step(250).withDefault(0).asTotalLimit(),
|
integerProperty("HDD").unit("GB") .min( 0).max(16000).step(250).withDefault(0).asTotalLimit(),
|
||||||
integerProperty("Traffic").unit("GB") .min(250).max(40000).step(250).required().asTotalLimit(),
|
integerProperty("Traffic").unit("GB") .min(250).max(40000).step(250).required().asTotalLimit(),
|
||||||
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").withDefault("BASIC")
|
|
||||||
|
// Alternatively we could specify it similarly to "Multi" option but exclusively counting:
|
||||||
|
// integerProperty("Resource-Points") .min(4).max(100).required()
|
||||||
|
// .each("CPUs").countsAs(64)
|
||||||
|
// .each("RAM").countsAs(64)
|
||||||
|
// .each("SSD").countsAs(18)
|
||||||
|
// .each("HDD").countsAs(2)
|
||||||
|
// .each("Traffic").countsAs(1),
|
||||||
|
|
||||||
|
integerProperty("SLA-Infrastructure EXT8H") .min( 0).max( 20).withDefault(0).asTotalLimitFor("SLA-Infrastructure", "EXT8H"),
|
||||||
|
integerProperty("SLA-Infrastructure EXT4H") .min( 0).max( 20).withDefault(0).asTotalLimitFor("SLA-Infrastructure", "EXT4H"),
|
||||||
|
integerProperty("SLA-Infrastructure EXT2H") .min( 0).max( 20).withDefault(0).asTotalLimitFor("SLA-Infrastructure", "EXT2H"),
|
||||||
|
|
||||||
|
integerProperty("SLA-Platform EXT8H") .min( 0).max( 20).withDefault(0).asTotalLimitFor("SLA-Platform", "EXT8H"),
|
||||||
|
integerProperty("SLA-Platform EXT4H") .min( 0).max( 20).withDefault(0).asTotalLimitFor("SLA-Platform", "EXT4H"),
|
||||||
|
integerProperty("SLA-Platform EXT2H") .min( 0).max( 20).withDefault(0).asTotalLimitFor("SLA-Platform", "EXT2H"),
|
||||||
|
|
||||||
|
integerProperty("SLA-EMail") .min( 0).max( 20).optional().asTotalLimitFor("SLA-Platform", "BASIC"),
|
||||||
|
integerProperty("SLA-Maria") .min( 0).max( 20).optional().asTotalLimitFor("SLA-Platform", "BASIC"),
|
||||||
|
integerProperty("SLA-PgSQL") .min( 0).max( 20).optional().asTotalLimitFor("SLA-Platform", "BASIC"),
|
||||||
|
integerProperty("SLA-Office") .min( 0).max( 20).optional().asTotalLimitFor("SLA-Platform", "BASIC"),
|
||||||
|
integerProperty("SLA-Web") .min( 0).max( 20).optional().asTotalLimitFor("SLA-Platform", "BASIC")
|
||||||
|
// @formatter:on
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,9 +65,9 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
|||||||
final var totalValue = ofNullable(hostingAsset.getSubHostingAssets()).orElse(emptyList())
|
final var totalValue = ofNullable(hostingAsset.getSubHostingAssets()).orElse(emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
.map(subItem -> propDef.getValue(subItem.getConfig()))
|
.map(subItem -> propDef.getValue(subItem.getConfig()))
|
||||||
.map(HsEntityValidator::toNonNullInteger)
|
.map(HsEntityValidator::toIntegerWithDefault0)
|
||||||
.reduce(0, Integer::sum);
|
.reduce(0, Integer::sum);
|
||||||
final var maxValue = getNonNullIntegerValue(propDef, hostingAsset.getConfig());
|
final var maxValue = getIntegerValueWithDefault0(propDef, hostingAsset.getConfig());
|
||||||
return totalValue > maxValue
|
return totalValue > maxValue
|
||||||
? "%s' maximum total is %d%s, but actual total is %s %d%s".formatted(
|
? "%s' maximum total is %d%s, but actual total is %s %d%s".formatted(
|
||||||
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
||||||
|
@ -15,10 +15,10 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
|
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
|
||||||
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
||||||
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
||||||
// stringProperty("monit_alarm_email").unit("GB").optional() TODO.spec: via Contact?
|
// stringProperty("monit_alarm_email").unit("GB").optional() FIXME: via Contact?
|
||||||
|
|
||||||
// other settings
|
// other settings
|
||||||
integerProperty("fastcgi_small").min(0).max(16).withDefault(4), // TODO.spec: check limits
|
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
|
||||||
|
|
||||||
// database software
|
// database software
|
||||||
booleanProperty("software-pgsql").withDefault(true),
|
booleanProperty("software-pgsql").withDefault(true),
|
||||||
@ -33,16 +33,16 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
booleanProperty("software-php-7.3").withDefault(false),
|
booleanProperty("software-php-7.3").withDefault(false),
|
||||||
booleanProperty("software-php-7.4").withDefault(true),
|
booleanProperty("software-php-7.4").withDefault(true),
|
||||||
booleanProperty("software-php-8.0").withDefault(false),
|
booleanProperty("software-php-8.0").withDefault(false),
|
||||||
booleanProperty("software-php-8.1").withDefault(true),
|
booleanProperty("software-php-8.1").withDefault(false),
|
||||||
booleanProperty("software-php-8.2").withDefault(false),
|
booleanProperty("software-php-8.2").withDefault(true),
|
||||||
|
|
||||||
// other software
|
// other software
|
||||||
// TODO.spec: booleanProperty("software-postfix-tls-1.0").withDefault(false),
|
booleanProperty("software-postfix-tls-1.0").withDefault(false),
|
||||||
// TODO.spec: booleanProperty("software-dovecot-tls-1.0").withDefault(false),
|
booleanProperty("software-dovecot-tls-1.0").withDefault(false),
|
||||||
booleanProperty("software-clamav").withDefault(true),
|
booleanProperty("software-clamav").withDefault(true),
|
||||||
booleanProperty("software-collabora").withDefault(false),
|
booleanProperty("software-collabora").withDefault(false),
|
||||||
booleanProperty("software-libreoffice").withDefault(false),
|
booleanProperty("software-libreoffice").withDefault(false),
|
||||||
booleanProperty("software-imagick-ghostscript").withDefault(true) // TODO.spec: default
|
booleanProperty("software-imagemagick-ghostscript").withDefault(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,18 +60,24 @@ public abstract class HsEntityValidator<E> {
|
|||||||
.orElse(emptyList()));
|
.orElse(emptyList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Integer getNonNullIntegerValue(final ValidatableProperty<?> prop, final Map<String, Object> propValues) {
|
protected static Integer getIntegerValueWithDefault0(final ValidatableProperty<?> prop, final Map<String, Object> propValues) {
|
||||||
final var value = prop.getValue(propValues);
|
final var value = prop.getValue(propValues);
|
||||||
if (value instanceof Integer) {
|
if (value instanceof Integer) {
|
||||||
return (Integer) value;
|
return (Integer) value;
|
||||||
}
|
}
|
||||||
|
if (value == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
throw new IllegalArgumentException(prop.propertyName + " Integer value expected, but got " + value);
|
throw new IllegalArgumentException(prop.propertyName + " Integer value expected, but got " + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Integer toNonNullInteger(final Object value) {
|
protected static Integer toIntegerWithDefault0(final Object value) {
|
||||||
if (value instanceof Integer) {
|
if (value instanceof Integer) {
|
||||||
return (Integer) value;
|
return (Integer) value;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Integer value expected, but got " + value);
|
if (value == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Integer value (or null) expected, but got " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import java.util.function.Function;
|
|||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public abstract class ValidatableProperty<T> {
|
public abstract class ValidatableProperty<T> {
|
||||||
@ -66,6 +67,28 @@ public abstract class ValidatableProperty<T> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValidatableProperty<T> asTotalLimitFor(final String propertyName, final String propertyValue) {
|
||||||
|
if (asTotalLimitValidators == null) {
|
||||||
|
asTotalLimitValidators = new ArrayList<>();
|
||||||
|
}
|
||||||
|
final TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> validator =
|
||||||
|
(final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
||||||
|
|
||||||
|
final var total = entity.getSubBookingItems().stream()
|
||||||
|
.map(server -> server.getResources().get(propertyName))
|
||||||
|
.filter(propertyValue::equals)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
final long limitingValue = ofNullable(prop.getValue(entity.getResources())).orElse(0);
|
||||||
|
if (total > factor*limitingValue) {
|
||||||
|
return List.of(limitingValue*factor + " total " + propertyName + "=" + propertyValue + " booked, but " + total + " utilized");
|
||||||
|
}
|
||||||
|
return emptyList();
|
||||||
|
};
|
||||||
|
asTotalLimitValidators.add((final HsBookingItemEntity entity) -> validator.apply(entity, (IntegerProperty)this, 1));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String propertyName() {
|
public String propertyName() {
|
||||||
return propertyName;
|
return propertyName;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
entry("CPUs", 4),
|
entry("CPUs", 4),
|
||||||
entry("RAM", 20),
|
entry("RAM", 20),
|
||||||
entry("SSD", 100),
|
entry("SSD", 100),
|
||||||
entry("Traffic", 5000)
|
entry("Traffic", 5000),
|
||||||
|
entry("SLA-Platform EXT4H", 2)
|
||||||
))
|
))
|
||||||
.subBookingItems(of(
|
.subBookingItems(of(
|
||||||
HsBookingItemEntity.builder()
|
HsBookingItemEntity.builder()
|
||||||
@ -41,7 +42,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
entry("CPUs", 2),
|
entry("CPUs", 2),
|
||||||
entry("RAM", 10),
|
entry("RAM", 10),
|
||||||
entry("SSD", 50),
|
entry("SSD", 50),
|
||||||
entry("Traffic", 2500)
|
entry("Traffic", 2500),
|
||||||
|
entry("SLA-Platform", "EXT4H")
|
||||||
))
|
))
|
||||||
.build(),
|
.build(),
|
||||||
HsBookingItemEntity.builder()
|
HsBookingItemEntity.builder()
|
||||||
@ -50,7 +52,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
entry("CPUs", 2),
|
entry("CPUs", 2),
|
||||||
entry("RAM", 10),
|
entry("RAM", 10),
|
||||||
entry("SSD", 50),
|
entry("SSD", 50),
|
||||||
entry("Traffic", 2500)
|
entry("Traffic", 2500),
|
||||||
|
entry("SLA-Platform", "EXT4H")
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
))
|
))
|
||||||
@ -73,7 +76,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
entry("CPUs", 4),
|
entry("CPUs", 4),
|
||||||
entry("RAM", 20),
|
entry("RAM", 20),
|
||||||
entry("SSD", 100),
|
entry("SSD", 100),
|
||||||
entry("Traffic", 5000)
|
entry("Traffic", 5000),
|
||||||
|
entry("SLA-Platform EXT2H", 1)
|
||||||
))
|
))
|
||||||
.subBookingItems(of(
|
.subBookingItems(of(
|
||||||
HsBookingItemEntity.builder()
|
HsBookingItemEntity.builder()
|
||||||
@ -82,7 +86,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
entry("CPUs", 3),
|
entry("CPUs", 3),
|
||||||
entry("RAM", 20),
|
entry("RAM", 20),
|
||||||
entry("SSD", 100),
|
entry("SSD", 100),
|
||||||
entry("Traffic", 3000)
|
entry("Traffic", 3000),
|
||||||
|
entry("SLA-Platform", "EXT2H")
|
||||||
))
|
))
|
||||||
.build(),
|
.build(),
|
||||||
HsBookingItemEntity.builder()
|
HsBookingItemEntity.builder()
|
||||||
@ -91,7 +96,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
entry("CPUs", 2),
|
entry("CPUs", 2),
|
||||||
entry("RAM", 10),
|
entry("RAM", 10),
|
||||||
entry("SSD", 50),
|
entry("SSD", 50),
|
||||||
entry("Traffic", 2500)
|
entry("Traffic", 2500),
|
||||||
|
entry("SLA-Platform", "EXT2H")
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
))
|
))
|
||||||
@ -105,8 +111,8 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
|||||||
"'D-12345:Test-Project:null.resources.CPUs' maximum total is 4, but actual total CPUs 5",
|
"'D-12345:Test-Project:null.resources.CPUs' maximum total is 4, but actual total CPUs 5",
|
||||||
"'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM 30 GB",
|
"'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM 30 GB",
|
||||||
"'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD 150 GB",
|
"'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD 150 GB",
|
||||||
"'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic 5500 GB"
|
"'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic 5500 GB",
|
||||||
|
"'D-12345:Test-Project:null.resources.1 total SLA-Platform=EXT2H booked, but 2 utilized"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user