/*
Copyright (c) 2015 Red Hat, Inc.
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 org.ovirt.engine.api.restapi.resource;
import java.util.List;
import java.util.Set;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.ovirt.engine.api.common.util.DetailHelper;
import org.ovirt.engine.api.model.Action;
import org.ovirt.engine.api.model.Certificate;
import org.ovirt.engine.api.model.Display;
import org.ovirt.engine.api.model.Fault;
import org.ovirt.engine.api.model.Statistic;
import org.ovirt.engine.api.model.Statistics;
import org.ovirt.engine.api.model.Template;
import org.ovirt.engine.api.model.Vm;
import org.ovirt.engine.api.resource.ActionResource;
import org.ovirt.engine.api.resource.AssignedAffinityLabelsResource;
import org.ovirt.engine.api.resource.AssignedPermissionsResource;
import org.ovirt.engine.api.resource.AssignedTagsResource;
import org.ovirt.engine.api.resource.CreationResource;
import org.ovirt.engine.api.resource.DiskAttachmentsResource;
import org.ovirt.engine.api.resource.SnapshotsResource;
import org.ovirt.engine.api.resource.StatisticsResource;
import org.ovirt.engine.api.resource.VmApplicationsResource;
import org.ovirt.engine.api.resource.VmCdromsResource;
import org.ovirt.engine.api.resource.VmDisksResource;
import org.ovirt.engine.api.resource.VmGraphicsConsolesResource;
import org.ovirt.engine.api.resource.VmHostDevicesResource;
import org.ovirt.engine.api.resource.VmNicsResource;
import org.ovirt.engine.api.resource.VmNumaNodesResource;
import org.ovirt.engine.api.resource.VmReportedDevicesResource;
import org.ovirt.engine.api.resource.VmResource;
import org.ovirt.engine.api.resource.VmSessionsResource;
import org.ovirt.engine.api.resource.VmWatchdogsResource;
import org.ovirt.engine.api.resource.externalhostproviders.KatelloErrataResource;
import org.ovirt.engine.api.restapi.logging.Messages;
import org.ovirt.engine.api.restapi.resource.externalhostproviders.BackendVmKatelloErrataResource;
import org.ovirt.engine.api.restapi.types.InitializationMapper;
import org.ovirt.engine.api.restapi.types.RngDeviceMapper;
import org.ovirt.engine.api.restapi.types.VmMapper;
import org.ovirt.engine.api.restapi.util.DisplayHelper;
import org.ovirt.engine.api.restapi.util.IconHelper;
import org.ovirt.engine.api.restapi.util.LinkHelper;
import org.ovirt.engine.api.restapi.util.ParametersHelper;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.ChangeVMClusterParameters;
import org.ovirt.engine.core.common.action.CloneVmParameters;
import org.ovirt.engine.core.common.action.MigrateVmParameters;
import org.ovirt.engine.core.common.action.MigrateVmToServerParameters;
import org.ovirt.engine.core.common.action.MoveOrCopyParameters;
import org.ovirt.engine.core.common.action.RemoveVmFromPoolParameters;
import org.ovirt.engine.core.common.action.RemoveVmParameters;
import org.ovirt.engine.core.common.action.RestoreAllSnapshotsParameters;
import org.ovirt.engine.core.common.action.RunVmOnceParams;
import org.ovirt.engine.core.common.action.RunVmParams;
import org.ovirt.engine.core.common.action.SetHaMaintenanceParameters;
import org.ovirt.engine.core.common.action.ShutdownVmParameters;
import org.ovirt.engine.core.common.action.StopVmParameters;
import org.ovirt.engine.core.common.action.StopVmTypeEnum;
import org.ovirt.engine.core.common.action.TryBackToAllSnapshotsOfVmParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VmManagementParametersBase;
import org.ovirt.engine.core.common.action.VmOperationParameterBase;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
import org.ovirt.engine.core.common.businessentities.HaMaintenanceMode;
import org.ovirt.engine.core.common.businessentities.InitializationType;
import org.ovirt.engine.core.common.businessentities.SnapshotActionEnum;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmStatic;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.queries.GetPermissionsForObjectParameters;
import org.ovirt.engine.core.common.queries.GetVmTemplateParameters;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.NameQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.compat.Guid;
public class BackendVmResource
extends AbstractBackendActionableResource<Vm, org.ovirt.engine.core.common.businessentities.VM>
implements VmResource {
private BackendVmsResource parent;
public static final String DETACH_ONLY = "detach_only";
public static final String FORCE = "force";
public static final String NEXT_RUN = "next_run";
public BackendVmResource(String id, BackendVmsResource parent) {
super(id, Vm.class, org.ovirt.engine.core.common.businessentities.VM.class);
this.parent = parent;
}
private boolean isNextRunRequested() {
return ParametersHelper.getBooleanParameter(httpHeaders, uriInfo, NEXT_RUN, true, false);
}
@Override
public Vm get() {
Vm vm;
if (isNextRunRequested()) {
org.ovirt.engine.core.common.businessentities.VM entity =
getEntity(org.ovirt.engine.core.common.businessentities.VM.class, VdcQueryType.GetVmNextRunConfiguration,
new IdQueryParameters(guid), id, true);
vm = addLinks(populate(VmMapper.map(entity, null, false), entity));
} else {
vm = performGet(VdcQueryType.GetVmByVmId, new IdQueryParameters(guid));
}
if (vm != null) {
DisplayHelper.adjustDisplayData(this, vm, isNextRunRequested());
removeRestrictedInfo(vm);
}
return vm;
}
private void removeRestrictedInfo(Vm vm) {
// Filtered users are not allowed to view host related information
if (isFiltered()) {
vm.setHost(null);
vm.setPlacementPolicy(null);
}
}
@Override
public Vm update(Vm incoming) {
validateParameters(incoming);
if (incoming.isSetCluster() && (incoming.getCluster().isSetId() || incoming.getCluster().isSetName())) {
Guid clusterId = lookupClusterId(incoming);
if(!clusterId.toString().equals(get().getCluster().getId())){
performAction(VdcActionType.ChangeVMCluster,
new ChangeVMClusterParameters(clusterId, guid, null)); // TODO: change 'null' to 'incoming.getVmCompa...' when REST support is added
}
}
if (!isFiltered()) {
if (incoming.isSetPlacementPolicy()) {
parent.validateAndUpdateHostsInPlacementPolicy(incoming.getPlacementPolicy());
}
} else {
incoming.setPlacementPolicy(null);
}
VdcQueryType queryType = isNextRunRequested()
? VdcQueryType.GetVmNextRunConfiguration
: VdcQueryType.GetVmByVmId;
Vm vm = performUpdate(
incoming,
new QueryIdResolver<>(queryType, IdQueryParameters.class),
VdcActionType.UpdateVm,
new UpdateParametersProvider()
);
if (vm != null) {
DisplayHelper.adjustDisplayData(this, vm, false);
removeRestrictedInfo(vm);
}
return vm;
}
@Override
public Response remove() {
get();
boolean force = ParametersHelper.getBooleanParameter(httpHeaders, uriInfo, FORCE, true, false);
RemoveVmParameters params = new RemoveVmParameters(guid, force);
// If detach only is set we do not remove the VM disks
boolean detachOnly = ParametersHelper.getBooleanParameter(httpHeaders, uriInfo, DETACH_ONLY, true, false);
if (detachOnly) {
params.setRemoveDisks(false);
}
return performAction(VdcActionType.RemoveVm, params);
}
private void validateParameters(Vm incoming) {
if (incoming.isSetDomain() && !incoming.getDomain().isSetName()) {
throw new WebFaultException(null,
localize(Messages.INCOMPLETE_PARAMS_REASON),
localize(Messages.INCOMPLETE_PARAMS_CONDITIONAL, "Domain", "Domain name"),
Response.Status.BAD_REQUEST);
}
if (!IconHelper.validateIconParameters(incoming)) {
throw new BaseBackendResource.WebFaultException(null,
localize(Messages.INVALID_ICON_PARAMETERS),
Response.Status.BAD_REQUEST);
}
}
protected Guid lookupClusterId(Vm vm) {
return vm.getCluster().isSetId() ? asGuid(vm.getCluster().getId())
: getEntity(Cluster.class,
VdcQueryType.GetClusterByName,
new NameQueryParameters(vm.getCluster().getName()),
"Cluster: name=" + vm.getCluster().getName()).getId();
}
@Override
public VmCdromsResource getCdromsResource() {
return inject(new BackendVmCdromsResource(guid));
}
@Override
public VmWatchdogsResource getWatchdogsResource() {
return inject(new BackendVmWatchdogsResource(guid));
}
public VmDisksResource getDisksResource() {
return inject(new BackendVmDisksResource(guid));
}
@Override
public DiskAttachmentsResource getDiskAttachmentsResource() {
return inject(new BackendDiskAttachmentsResource(guid));
}
@Override
public VmNicsResource getNicsResource() {
return inject(new BackendVmNicsResource(guid));
}
@Override
public SnapshotsResource getSnapshotsResource() {
return inject(new BackendSnapshotsResource(guid));
}
@Override
public AssignedTagsResource getTagsResource() {
return inject(new BackendVmTagsResource(id));
}
@Override
public VmApplicationsResource getApplicationsResource() {
return inject(new BackendVmApplicationsResource(guid));
}
@Override
public AssignedPermissionsResource getPermissionsResource() {
return inject(new BackendAssignedPermissionsResource(guid,
VdcQueryType.GetPermissionsForObject,
new GetPermissionsForObjectParameters(guid),
Vm.class,
VdcObjectType.VM));
}
@Override
public CreationResource getCreationResource(String ids) {
return inject(new BackendCreationResource(ids));
}
@Override
public ActionResource getActionResource(String action, String ids) {
return inject(new BackendActionResource(action, ids));
}
@Override
public StatisticsResource getStatisticsResource() {
EntityIdResolver<Guid> resolver = new QueryIdResolver<>(VdcQueryType.GetVmByVmId, IdQueryParameters.class);
VmStatisticalQuery query = new VmStatisticalQuery(resolver, newModel(id));
return inject(new BackendStatisticsResource<>(entityType, guid, query));
}
@Override
public Response migrate(Action action) {
boolean forceMigration = action.isSetForce() ? action.isForce() : false;
if (!action.isSetHost()) {
return doAction(VdcActionType.MigrateVm,
new MigrateVmParameters(forceMigration, guid, getTargetClusterId(action)),
action);
} else {
return doAction(VdcActionType.MigrateVmToServer,
new MigrateVmToServerParameters(forceMigration, guid, getHostId(action), getTargetClusterId(action)),
action);
}
}
private Guid getTargetClusterId(Action action) {
if (action.isSetCluster() && action.getCluster().isSetId()) {
return asGuid(action.getCluster().getId());
}
// means use the cluster of the provided host
return null;
}
@Override
public Response shutdown(Action action) {
// REVISIT add waitBeforeShutdown Action paramater
// to api schema before next sub-milestone
return doAction(VdcActionType.ShutdownVm,
new ShutdownVmParameters(guid, true),
action);
}
@Override
public Response reboot(Action action) {
return doAction(VdcActionType.RebootVm,
new VmOperationParameterBase(guid),
action);
}
@Override
public Response undoSnapshot(Action action) {
RestoreAllSnapshotsParameters restoreParams = new RestoreAllSnapshotsParameters(guid, SnapshotActionEnum.UNDO);
Response response = doAction(VdcActionType.RestoreAllSnapshots,
restoreParams,
action);
return response;
}
@Override
public Response doClone(Action action) {
validateParameters(action, "vm.name");
org.ovirt.engine.core.common.businessentities.VM vm = getEntity(
org.ovirt.engine.core.common.businessentities.VM.class,
VdcQueryType.GetVmByVmId,
new IdQueryParameters(guid), "VM: id=" + guid);
CloneVmParameters cloneVmParameters = new CloneVmParameters(vm, action.getVm().getName());
cloneVmParameters.setMakeCreatorExplicitOwner(isFiltered());
Response response = doAction(VdcActionType.CloneVm,
cloneVmParameters,
action);
return response;
}
@Override
public Response reorderMacAddresses(Action action) {
getEntity(org.ovirt.engine.core.common.businessentities.VM.class,
VdcQueryType.GetVmByVmId,
new IdQueryParameters(guid),
"VM: id=" + guid,
true);
final VmOperationParameterBase params = new VmOperationParameterBase(guid);
final Response response = doAction(
VdcActionType.ReorderVmNics,
params,
action);
return response;
}
@Override
public Response commitSnapshot(Action action) {
RestoreAllSnapshotsParameters restoreParams = new RestoreAllSnapshotsParameters(guid, SnapshotActionEnum.COMMIT);
Response response = doAction(VdcActionType.RestoreAllSnapshots,
restoreParams,
action);
return response;
}
@Override
public Response previewSnapshot(Action action) {
validateParameters(action, "snapshot.id");
TryBackToAllSnapshotsOfVmParameters tryBackParams =
new TryBackToAllSnapshotsOfVmParameters(guid, asGuid(action.getSnapshot().getId()));
if (action.isSetRestoreMemory()) {
tryBackParams.setRestoreMemory(action.isRestoreMemory());
}
if (action.isSetDisks()) {
tryBackParams.setDisks(getParent().mapDisks(action.getDisks()));
}
Response response = doAction(VdcActionType.TryBackToAllSnapshotsOfVm,
tryBackParams,
action);
return response;
}
@Override
public Response start(Action action) {
RunVmParams params;
VdcActionType actionType;
if (action.isSetVm()) {
Vm vm = action.getVm();
actionType = VdcActionType.RunVmOnce;
params = createRunVmOnceParams(vm);
} else {
actionType = VdcActionType.RunVm;
params = new RunVmParams(guid);
}
if (action.isSetPause() && action.isPause()) {
params.setRunAndPause(true);
}
boolean useSysprep = action.isSetUseSysprep() && action.isUseSysprep();
boolean useCloudInit = action.isSetUseCloudInit() && action.isUseCloudInit();
if (useSysprep && useCloudInit) {
Fault fault = new Fault();
fault.setReason(localize(Messages.CANT_USE_SYSPREP_AND_CLOUD_INIT_SIMULTANEOUSLY));
return Response.status(Response.Status.CONFLICT).entity(fault).build();
}
if (useSysprep) {
params.setInitializationType(InitializationType.Sysprep);
}
else if (useCloudInit) {
params.setInitializationType(InitializationType.CloudInit);
}
else {
params.setInitializationType(InitializationType.None);
}
return doAction(actionType, params, action);
}
private RunVmOnceParams createRunVmOnceParams(Vm vm) {
VM entity = getEntity(entityType, VdcQueryType.GetVmByVmId, new IdQueryParameters(guid), id, true);
RunVmOnceParams params = map(vm, map(map(entity, new Vm()),
new RunVmOnceParams(guid)));
if (vm.isSetPlacementPolicy()) {
Set<Guid> hostsGuidsSet = parent.validateAndUpdateHostsInPlacementPolicy(vm.getPlacementPolicy());
if (hostsGuidsSet.size() > 0) {
// take the arbitrary first host for run destination
params.setDestinationVdsId(hostsGuidsSet.iterator().next());
}
}
if (vm.isSetInitialization()) {
if (vm.getInitialization().isSetCloudInit()) {
params.setInitializationType(InitializationType.CloudInit);
}
params.setVmInit(InitializationMapper.map(vm.getInitialization(), entity.getVmInit()));
}
return params;
}
@Override
public Response stop(Action action) {
return doAction(VdcActionType.StopVm,
new StopVmParameters(guid, StopVmTypeEnum.NORMAL),
action);
}
@Override
public Response suspend(Action action) {
return doAction(VdcActionType.HibernateVm,
new VmOperationParameterBase(guid),
action);
}
@Override
public Response detach(Action action) {
return doAction(VdcActionType.RemoveVmFromPool,
new RemoveVmFromPoolParameters(guid, true, true),
action);
}
@Override
public Response export(Action action) {
validateParameters(action, "storageDomain.id|name");
MoveOrCopyParameters params = new MoveOrCopyParameters(guid, getStorageDomainId(action));
if (action.isSetExclusive() && action.isExclusive()) {
params.setForceOverride(true);
}
if (action.isSetDiscardSnapshots() && action.isDiscardSnapshots()) {
params.setCopyCollapse(true);
}
return doAction(VdcActionType.ExportVm, params, action);
}
@Override
public Response ticket(Action action) {
return BackendGraphicsConsoleHelper.setTicket(this, action, guid, deriveGraphicsType());
}
private GraphicsType deriveGraphicsType() {
org.ovirt.engine.core.common.businessentities.VM vm = getEntity(org.ovirt.engine.core.common.businessentities.VM.class,
VdcQueryType.GetVmByVmId, new IdQueryParameters(guid), "GetVmByVmId");
return (vm == null)
? null
: VmMapper.deriveGraphicsType(vm.getGraphicsInfos());
}
@Override
public Response logon(Action action) {
final Response response = doAction(VdcActionType.VmLogon,
new VmOperationParameterBase(guid),
action);
return response;
}
@Override
public Response freezeFilesystems(Action action) {
final Response response = doAction(VdcActionType.FreezeVm,
new VmOperationParameterBase(guid),
action);
return response;
}
@Override
public Response thawFilesystems(Action action) {
final Response response = doAction(VdcActionType.ThawVm,
new VmOperationParameterBase(guid),
action);
return response;
}
protected RunVmOnceParams map(Vm vm, RunVmOnceParams params) {
return getMapper(Vm.class, RunVmOnceParams.class).map(vm, params);
}
@Override
protected Vm doPopulate(Vm model, org.ovirt.engine.core.common.businessentities.VM entity) {
BackendVmDeviceHelper.setConsoleDevice(this, model);
BackendVmDeviceHelper.setVirtioScsiController(this, model);
BackendVmDeviceHelper.setSoundcard(this, model);
BackendVmDeviceHelper.setRngDevice(this, model);
parent.setVmOvfConfiguration(model, entity);
return model;
}
@Override
protected Vm deprecatedPopulate(Vm model, org.ovirt.engine.core.common.businessentities.VM entity) {
Set<String> details = DetailHelper.getDetails(httpHeaders, uriInfo);
if (details.contains("statistics")) {
addStatistics(model, entity);
}
BackendVmDeviceHelper.setPayload(this, model);
BackendVmDeviceHelper.setCertificateInfo(this, model);
MemoryPolicyHelper.setupMemoryBalloon(model, this);
return model;
}
private void addStatistics(Vm model, org.ovirt.engine.core.common.businessentities.VM entity) {
model.setStatistics(new Statistics());
VmStatisticalQuery query = new VmStatisticalQuery(newModel(model.getId()));
List<Statistic> statistics = query.getStatistics(entity);
for (Statistic statistic : statistics) {
LinkHelper.addLinks(statistic, query.getParentType());
}
model.getStatistics().getStatistics().addAll(statistics);
}
protected class UpdateParametersProvider implements
ParametersProvider<Vm, org.ovirt.engine.core.common.businessentities.VM> {
@Override
public VdcActionParametersBase getParameters(Vm incoming,
org.ovirt.engine.core.common.businessentities.VM entity) {
VmStatic updated = getMapper(modelType, VmStatic.class).map(incoming,
entity.getStaticData());
updated.setUsbPolicy(VmMapper.getUsbPolicyOnUpdate(incoming.getUsb(), entity.getUsbPolicy()));
VmManagementParametersBase params = new VmManagementParametersBase(updated);
params.setApplyChangesLater(isNextRunRequested());
if (incoming.isSetPayloads()) {
if (incoming.isSetPayloads() && incoming.getPayloads().isSetPayloads()) {
params.setVmPayload(parent.getPayload(incoming));
} else {
params.setClearPayload(true);
}
}
if (incoming.isSetMemoryPolicy() && incoming.getMemoryPolicy().isSetBallooning()) {
params.setBalloonEnabled(incoming.getMemoryPolicy().isBallooning());
}
if (incoming.isSetConsole() && incoming.getConsole().isSetEnabled()) {
params.setConsoleEnabled(incoming.getConsole().isEnabled());
}
if (incoming.isSetVirtioScsi()) {
if (incoming.getVirtioScsi().isSetEnabled()) {
params.setVirtioScsiEnabled(incoming.getVirtioScsi().isEnabled());
}
}
if (incoming.isSetSoundcardEnabled()) {
params.setSoundDeviceEnabled(incoming.isSoundcardEnabled());
}
if (incoming.isSetRngDevice()) {
params.setUpdateRngDevice(true);
params.setRngDevice(RngDeviceMapper.map(incoming.getRngDevice(), null));
}
DisplayHelper.setGraphicsToParams(incoming.getDisplay(), params);
if (incoming.isSetInstanceType() && (incoming.getInstanceType().isSetId() || incoming.getInstanceType().isSetName())) {
updated.setInstanceTypeId(lookupInstanceTypeId(incoming.getInstanceType()));
} else if (incoming.isSetInstanceType()) {
// this means that the instance type should be unset
updated.setInstanceTypeId(null);
}
IconHelper.setIconToParams(incoming, params);
return params;
}
}
private Guid lookupInstanceTypeId(Template template) {
return template.isSetId() ? asGuid(template.getId()) : lookupInstanceTypeByName(template).getId();
}
private VmTemplate lookupInstanceTypeByName(Template template) {
return getEntity(VmTemplate.class,
VdcQueryType.GetInstanceType,
new GetVmTemplateParameters(template.getName()),
"GetVmTemplate");
}
@Override
public Response cancelMigration(Action action) {
return doAction(VdcActionType.CancelMigrateVm,
new VmOperationParameterBase(guid), action);
}
public BackendVmsResource getParent() {
return parent;
}
public void setCertificateInfo(Vm model) {
VdcQueryReturnValue result =
runQuery(VdcQueryType.GetVdsCertificateSubjectByVmId,
new IdQueryParameters(asGuid(model.getId())));
if (result != null && result.getSucceeded() && result.getReturnValue() != null) {
if (!model.isSetDisplay()) {
model.setDisplay(new Display());
}
model.getDisplay().setCertificate(new Certificate());
model.getDisplay().getCertificate().setSubject(result.getReturnValue().toString());
}
}
@Override
public VmReportedDevicesResource getReportedDevicesResource() {
return inject(new BackendVmReportedDevicesResource(guid));
}
@Override
public VmSessionsResource getSessionsResource() {
return inject(new BackendVmSessionsResource(guid));
}
@Override
public VmGraphicsConsolesResource getGraphicsConsolesResource() {
return inject(new BackendVmGraphicsConsolesResource(guid));
}
@Override
public Response maintenance(Action action) {
validateParameters(action, "maintenanceEnabled");
org.ovirt.engine.core.common.businessentities.VM entity =
getEntity(org.ovirt.engine.core.common.businessentities.VM.class,
VdcQueryType.GetVmByVmId,
new IdQueryParameters(guid),
id);
if (!entity.isHostedEngine()) {
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity("Moving to maintenance mode is currently only available for the VM containing the hosted engine.")
.build());
}
return doAction(VdcActionType.SetHaMaintenance,
new SetHaMaintenanceParameters(entity.getRunOnVds(),
HaMaintenanceMode.GLOBAL, action.isMaintenanceEnabled()),
action);
}
@Override
public VmNumaNodesResource getNumaNodesResource() {
return inject(new BackendVmNumaNodesResource(guid));
}
@Override
public KatelloErrataResource getKatelloErrataResource() {
return inject(new BackendVmKatelloErrataResource(id));
}
@Override
public VmHostDevicesResource getHostDevicesResource() {
return inject(new BackendVmHostDevicesResource(guid));
}
@Override
public AssignedAffinityLabelsResource getAffinityLabelsResource() {
return inject(new BackendAssignedAffinityLabelsResource(id, VM::new));
}
}