
com.vmware.photon.controller.model.resources.ResourcePoolService Maven / Gradle / Ivy
/*
* Copyright (c) 2015-2016 VMware, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, without warranties or
* conditions of any kind, EITHER EXPRESS OR IMPLIED. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.vmware.photon.controller.model.resources;
import java.util.EnumSet;
import com.vmware.photon.controller.model.UriPaths;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.photon.controller.model.resources.ResourcePoolService.ResourcePoolState.ResourcePoolProperty;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.ServiceDocumentDescription.PropertyUsageOption;
import com.vmware.xenon.common.StatefulService;
import com.vmware.xenon.common.Utils;
import com.vmware.xenon.services.common.QueryTask.Query;
/**
* Describes a resource pool. A resource pool is a grouping of {@link ComputeState}s that can be
* used as a single unit for planning and allocation purposes.
*
* {@link ComputeState}s that contribute capacity to this resource pool are found by
* executing the {@link ResourcePoolState#query} query. For non-elastic resource pools
* the query is auto-generated by using the {@link ComputeState#resourcePoolLink}. For
* elastic resource pools the query is provided by the resource pool creator.
*
*
Thus a resource may participate in at most one non-elastic resource pool and zero or more
* elastic resource pools.
*/
public class ResourcePoolService extends StatefulService {
public static final String FACTORY_LINK = UriPaths.RESOURCES + "/pools";
/**
* This class represents the document state associated with a
* {@link ResourcePoolService} task.
*/
public static class ResourcePoolState extends ResourceState {
public static final String FIELD_NAME_PROPERTIES = "properties";
/**
* Enumeration used to define properties of the resource pool.
*/
public enum ResourcePoolProperty {
/**
* An elastic resource pool uses a dynamic query to find the participating resources.
* The {@link ComputeState#resourcePoolLink} field of the returned resources may not
* match this resource pool instance.
*/
ELASTIC
}
/**
* Project name of this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public String projectName;
/**
* Properties of this resource pool, if it is elastic, etc.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public EnumSet properties;
/**
* Minimum number of CPU Cores in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long minCpuCount;
/**
* Minimum number of GPU Cores in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long minGpuCount;
/**
* Minimum amount of memory (in bytes) in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long minMemoryBytes;
/**
* Minimum disk capacity (in bytes) in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long minDiskCapacityBytes;
/**
* Maximum number of CPU Cores in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long maxCpuCount;
/**
* Maximum number of GPU Cores in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long maxGpuCount;
/**
* Maximum amount of memory (in bytes) in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long maxMemoryBytes;
/**
* Maximum disk capacity (in bytes) in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Long maxDiskCapacityBytes;
/**
* Maximum CPU Cost (per minute) in this resource pool.
*/
@Deprecated
public Double maxCpuCostPerMinute;
/**
* Maximum Disk cost (per minute) in this resource pool.
*/
@Deprecated
public Double maxDiskCostPerMinute;
/**
* Currency unit used for pricing.
*/
@Deprecated
public String currencyUnit;
/**
* Query to use to retrieve resources in this resource pool.
*/
@UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
public Query query;
}
public ResourcePoolService() {
super(ResourcePoolState.class);
super.toggleOption(ServiceOption.PERSISTENCE, true);
super.toggleOption(ServiceOption.REPLICATION, true);
super.toggleOption(ServiceOption.OWNER_SELECTION, true);
super.toggleOption(ServiceOption.IDEMPOTENT_POST, true);
}
@Override
public void handleDelete(Operation delete) {
logInfo("Deleting ResourcePool, Path: %s, Operation ID: %d, Referrer: %s",
delete.getUri().getPath(), delete.getId(),
delete.getRefererAsString());
super.handleDelete(delete);
}
@Override
public void handleCreate(Operation createPost) {
try {
processInput(createPost);
createPost.complete();
} catch (Throwable t) {
createPost.fail(t);
}
}
@Override
public void handlePut(Operation put) {
try {
ResourcePoolState returnState = processInput(put);
setState(put, returnState);
put.complete();
} catch (Throwable t) {
put.fail(t);
}
}
private ResourcePoolState processInput(Operation op) {
if (!op.hasBody()) {
throw (new IllegalArgumentException("body is required"));
}
ResourcePoolState state = op.getBody(ResourcePoolState.class);
validateState(state);
if (!state.properties.contains(ResourcePoolProperty.ELASTIC)) {
state.query = generateResourcePoolQuery(state);
}
return state;
}
@Override
public void handlePatch(Operation patch) {
ResourcePoolState currentState = getState(patch);
if (!currentState.properties.contains(ResourcePoolProperty.ELASTIC)) {
// clean auto-generated query to catch patches with unexpected query
currentState.query = null;
}
// use standard resource merging with an additional custom handler for the query
ResourceUtils.handlePatch(patch, currentState, getStateDescription(),
ResourcePoolState.class,
op -> {
// check state and re-generate the query, if needed
validateState(currentState);
if (!currentState.properties.contains(ResourcePoolProperty.ELASTIC)) {
currentState.query = generateResourcePoolQuery(currentState);
}
// don't report a state change, it is already reported if resource pool type has
// changed
return false;
});
}
public void validateState(ResourcePoolState state) {
Utils.validateState(getStateDescription(), state);
if (state.name == null) {
throw new IllegalArgumentException("Resource pool name is required.");
}
if (state.properties == null) {
state.properties = EnumSet
.noneOf(ResourcePoolState.ResourcePoolProperty.class);
}
if (state.properties.contains(ResourcePoolProperty.ELASTIC)) {
if (state.query == null) {
throw new IllegalArgumentException("Query is required for elastic resource pools.");
}
}
}
/**
* Generates a query that finds all computes which resource pool link points to this
* resource pool. Applicable to non-elastic pools only.
*/
private Query generateResourcePoolQuery(ResourcePoolState initState) {
Query query = Query.Builder.create()
.addKindFieldClause(ComputeState.class)
.addFieldClause(ComputeState.FIELD_NAME_RESOURCE_POOL_LINK, getSelfLink())
.build();
return query;
}
}