shwstppr commented on code in PR #13907: URL: https://github.com/apache/cloudstack/pull/13907#discussion_r3901094080
########## server/src/main/java/org/apache/cloudstack/vm/bootgroup/InstanceBootGroupApiServiceImpl.java: ########## @@ -0,0 +1,909 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.vm.bootgroup; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.command.user.bootgroup.AddMemberToInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.CreateInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.CreateInstanceBootGroupReadinessRuleCmd; +import org.apache.cloudstack.api.command.user.bootgroup.DeleteInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.DeleteInstanceBootGroupReadinessRuleCmd; +import org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupMembersCmd; +import org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupReadinessRulesCmd; +import org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupsCmd; +import org.apache.cloudstack.api.command.user.bootgroup.RebootInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.RemoveInstanceBootGroupMemberCmd; +import org.apache.cloudstack.api.command.user.bootgroup.StartInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.StopInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupMemberCmd; +import org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupReadinessRuleCmd; +import org.apache.cloudstack.api.query.dao.InstanceBootGroupJoinDao; +import org.apache.cloudstack.api.query.vo.InstanceBootGroupJoinVO; +import org.apache.cloudstack.api.response.InstanceBootGroupMemberChildResponse; +import org.apache.cloudstack.api.response.InstanceBootGroupMemberResponse; +import org.apache.cloudstack.api.response.InstanceBootGroupReadinessRuleResponse; +import org.apache.cloudstack.api.response.InstanceBootGroupResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.bootgroup.readiness.InstanceBootGroupReadinessRule; +import org.apache.cloudstack.vm.bootgroup.readiness.InstanceBootGroupReadinessRuleService; +import org.apache.cloudstack.vm.bootgroup.readiness.ReadinessChecker; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +import com.cloud.api.ApiResponseHelper; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.Project; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.vm.InstanceGroupVMMapVO; +import com.cloud.vm.InstanceGroupVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.InstanceBootGroupDao; +import com.cloud.vm.dao.InstanceBootGroupDetailsDao; +import com.cloud.vm.dao.InstanceBootGroupMemberDao; +import com.cloud.vm.dao.InstanceBootGroupReadinessCheckResultDao; +import com.cloud.vm.dao.InstanceBootGroupReadinessRuleDao; +import com.cloud.vm.dao.InstanceBootGroupReadinessRuleDetailsDao; +import com.cloud.vm.dao.InstanceGroupDao; +import com.cloud.vm.dao.InstanceGroupVMMapDao; +import com.cloud.vm.dao.UserVmDao; + +/** + * API-facing half of the Instance Boot Group feature: ACL, param validation, response building, + * command registration. Delegates orchestration/hypervisor work to {@link InstanceBootGroupManager} + * and membership eligibility checks to {@link InstanceBootGroupMembershipGuard}. + */ +@Component +public class InstanceBootGroupApiServiceImpl implements InstanceBootGroupService, PluggableService { + + @Inject + private InstanceBootGroupDao instanceBootGroupDao; + + @Inject + private InstanceBootGroupJoinDao instanceBootGroupJoinDao; + + @Inject + private InstanceBootGroupMemberDao instanceBootGroupMemberDao; + + @Inject + private AccountManager accountManager; + + @Inject + private UserVmDao userVmDao; + + @Inject + private InstanceGroupDao instanceGroupDao; + + @Inject + private InstanceBootGroupManager instanceBootGroupManager; + + @Inject + private InstanceBootGroupMembershipGuard instanceBootGroupMembershipGuard; + + @Inject + private InstanceBootGroupReadinessRuleService instanceBootGroupReadinessRuleService; + + @Inject + private InstanceBootGroupReadinessRuleDao instanceBootGroupReadinessRuleDao; + + @Inject + private InstanceBootGroupReadinessRuleDetailsDao instanceBootGroupReadinessRuleDetailsDao; + + @Inject + private InstanceBootGroupReadinessCheckResultDao instanceBootGroupReadinessCheckResultDao; + + @Inject + private InstanceBootGroupDetailsDao instanceBootGroupDetailsDao; + + @Inject + private InstanceGroupVMMapDao instanceGroupVMMapDao; + + @NotNull + protected InstanceBootGroupVO getGroupAndCheckAccess(long id) { + InstanceBootGroupVO group = instanceBootGroupDao.findById(id); + if (group == null) { + throw new InvalidParameterValueException("Unable to find instance boot group with ID: " + id); + } + Account caller = CallContext.current().getCallingAccount(); + accountManager.checkAccess(caller, null, true, group); + return group; + } + + protected InstanceBootGroupResponse createInstanceBootGroupResponse(InstanceBootGroupJoinVO bootGroup) { + InstanceBootGroupResponse response = new InstanceBootGroupResponse(); + response.setId(bootGroup.getUuid()); + response.setName(bootGroup.getName()); + response.setDescription(bootGroup.getDescription()); + response.setCreated(bootGroup.getCreated()); + ApiResponseHelper.populateOwner(response, bootGroup); + + String timeoutOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key()); + response.setReadinessAttemptTimeoutSeconds(timeoutOverride != null ? Long.parseLong(timeoutOverride) : InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.value()); + String maxRetryOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key()); + response.setReadinessMaxRetryAttempts(maxRetryOverride != null ? Long.parseLong(maxRetryOverride) : InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.value()); + String rebootOnRetryOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key()); + response.setReadinessRebootOnRetry(rebootOnRetryOverride != null ? Boolean.parseBoolean(rebootOnRetryOverride) : InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.value()); + String initialDelayOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key()); + response.setReadinessInitialDelaySeconds(initialDelayOverride != null ? Long.parseLong(initialDelayOverride) : InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.value()); + + response.setObjectName("instancebootgroup"); + return response; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_CREATE, eventDescription = "creating Instance Boot Group") + public InstanceBootGroup createInstanceBootGroup(CreateInstanceBootGroupCmd cmd) { + Account caller = CallContext.current().getCallingAccount(); + Account owner = accountManager.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); + + if (instanceBootGroupDao.isNameInUse(owner.getId(), cmd.getName())) { + throw new InvalidParameterValueException("An instance boot group with name '" + cmd.getName() + "' already exists in this account"); + } + + return Transaction.execute((TransactionCallback<InstanceBootGroupVO>) status -> { + InstanceBootGroupVO group = new InstanceBootGroupVO(cmd.getName(), cmd.getDescription(), owner.getId(), owner.getDomainId()); + group = instanceBootGroupDao.persist(group); + CallContext.current().setEventResourceId(group.getId()); + + if (cmd.getReadinessAttemptTimeoutSeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key(), cmd.getReadinessAttemptTimeoutSeconds()); + } + if (cmd.getReadinessMaxRetryAttempts() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key(), cmd.getReadinessMaxRetryAttempts()); + } + if (cmd.getReadinessRebootOnRetry() != null) { + instanceBootGroupDetailsDao.setDetail(group.getId(), InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key(), String.valueOf(cmd.getReadinessRebootOnRetry())); + } + if (cmd.getReadinessInitialDelaySeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key(), cmd.getReadinessInitialDelaySeconds()); + } + + return group; + }); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_DELETE, eventDescription = "deleting Instance Boot Group") + public boolean deleteInstanceBootGroup(DeleteInstanceBootGroupCmd cmd) { + InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId()); + return Transaction.execute((TransactionCallback<Boolean>) status -> { + instanceBootGroupMemberDao.deleteByBootGroupId(group.getId()); + instanceBootGroupDao.remove(group.getId()); + return true; + }); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_UPDATE, eventDescription = "updating Instance Boot Group") + public InstanceBootGroup updateInstanceBootGroup(UpdateInstanceBootGroupCmd cmd) { + InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId()); + + if (cmd.getName() != null && !Objects.equals(cmd.getName(), group.getName())) { + Account owner = accountManager.getAccount(group.getAccountId()); + if (instanceBootGroupDao.isNameInUse(owner.getId(), cmd.getName())) { + throw new InvalidParameterValueException("An instance boot group with name '" + cmd.getName() + "' already exists in this account"); + } + group.setName(cmd.getName()); + } + if (cmd.getDescription() != null) { + group.setDescription(cmd.getDescription()); + } + + return Transaction.execute((TransactionCallback<InstanceBootGroupVO>) status -> { + if (cmd.getReadinessAttemptTimeoutSeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key(), cmd.getReadinessAttemptTimeoutSeconds()); + } + if (cmd.getReadinessMaxRetryAttempts() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key(), cmd.getReadinessMaxRetryAttempts()); + } + if (cmd.getReadinessRebootOnRetry() != null) { + instanceBootGroupDetailsDao.setDetail(group.getId(), InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key(), String.valueOf(cmd.getReadinessRebootOnRetry())); + } + if (cmd.getReadinessInitialDelaySeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key(), cmd.getReadinessInitialDelaySeconds()); + } + + instanceBootGroupDao.update(group.getId(), group); + return instanceBootGroupDao.findById(group.getId()); + }); + } + + private void setOrClearOverride(long bootGroupId, String key, long value) { + if (value < 0) { + instanceBootGroupDetailsDao.setDetail(bootGroupId, key, null); + } else { + instanceBootGroupDetailsDao.setDetail(bootGroupId, key, String.valueOf(value)); + } + } + + @Override + public ListResponse<InstanceBootGroupResponse> listInstanceBootGroups(ListInstanceBootGroupsCmd cmd) { + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + final Long id = cmd.getId(); + final String keyword = cmd.getKeyword(); + + List<InstanceBootGroupResponse> responsesList = new ArrayList<>(); + List<Long> permittedAccounts = new ArrayList<>(); + Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = + new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); + accountManager.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), + permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); + Long domainId = domainIdRecursiveListProject.first(); + Boolean isRecursive = domainIdRecursiveListProject.second(); + Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + + Filter searchFilter = new Filter(InstanceBootGroupJoinVO.class, "id", true, cmd.getStartIndex(), + cmd.getPageSizeVal()); + SearchBuilder<InstanceBootGroupJoinVO> sb = instanceBootGroupJoinDao.createSearchBuilder(); + accountManager.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, + listProjectResourcesCriteria); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE); + SearchCriteria<InstanceBootGroupJoinVO> sc = sb.create(); + accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, + listProjectResourcesCriteria); + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + if (id != null) { + sc.setParameters("id", id); + } + Pair<List<InstanceBootGroupJoinVO>, Integer> bootGroupsAndCount = instanceBootGroupJoinDao.searchAndCount(sc, searchFilter); + for (InstanceBootGroupJoinVO bootGroup : bootGroupsAndCount.first()) { + InstanceBootGroupResponse response = createInstanceBootGroupResponse(bootGroup); + responsesList.add(response); + } + ListResponse<InstanceBootGroupResponse> response = new ListResponse<>(); + response.setResponses(responsesList, bootGroupsAndCount.second()); + return response; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_MEMBER_ADD, eventDescription = "adding Instance Boot Group member") + public InstanceBootGroupMember addMemberToInstanceBootGroup(AddMemberToInstanceBootGroupCmd cmd) { + InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId()); + + if (cmd.getOrder() < 0) { + throw new InvalidParameterValueException("Order value must be 0 or greater"); + } + + InstanceBootGroupMember.MemberType memberType; + long memberId; + + validateEitherVirtualMachineIdOrInstanceGroupIdParam(cmd.getVirtualMachineId(), cmd.getInstanceGroupId()); + + if (cmd.getVirtualMachineId() != null) { + UserVm vm = getValidatedVmForAddMember(group, cmd.getVirtualMachineId()); + memberType = InstanceBootGroupMember.MemberType.VirtualMachine; + memberId = vm.getId(); + } else { + InstanceGroupVO instanceGroup = getValidatedInstanceGroupAddMember(group, cmd.getInstanceGroupId()); + memberType = InstanceBootGroupMember.MemberType.InstanceGroup; + memberId = instanceGroup.getId(); + } + + if (instanceBootGroupMemberDao.findByMember(memberType, memberId) != null) { + throw new InvalidParameterValueException(String.format("This %s already belongs to an instance boot group", memberType.name())); + } + + List<InstanceBootGroupMemberVO> siblings = instanceBootGroupMemberDao.listByBootGroupId(group.getId()); + long maxMembers = InstanceBootGroupManagerImpl.MaxMembersPerBootGroup.valueIn(group.getDomainId()); + if (siblings.size() >= maxMembers) { + throw new InvalidParameterValueException(String.format( + "Instance boot group %s already has the maximum of %d member(s) allowed", group, maxMembers)); + } + + shiftSiblingOrdersForInsert(siblings, cmd.getOrder()); Review Comment: done ########## server/src/main/java/org/apache/cloudstack/vm/bootgroup/InstanceBootGroupApiServiceImpl.java: ########## @@ -0,0 +1,909 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.vm.bootgroup; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.command.user.bootgroup.AddMemberToInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.CreateInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.CreateInstanceBootGroupReadinessRuleCmd; +import org.apache.cloudstack.api.command.user.bootgroup.DeleteInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.DeleteInstanceBootGroupReadinessRuleCmd; +import org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupMembersCmd; +import org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupReadinessRulesCmd; +import org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupsCmd; +import org.apache.cloudstack.api.command.user.bootgroup.RebootInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.RemoveInstanceBootGroupMemberCmd; +import org.apache.cloudstack.api.command.user.bootgroup.StartInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.StopInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupCmd; +import org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupMemberCmd; +import org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupReadinessRuleCmd; +import org.apache.cloudstack.api.query.dao.InstanceBootGroupJoinDao; +import org.apache.cloudstack.api.query.vo.InstanceBootGroupJoinVO; +import org.apache.cloudstack.api.response.InstanceBootGroupMemberChildResponse; +import org.apache.cloudstack.api.response.InstanceBootGroupMemberResponse; +import org.apache.cloudstack.api.response.InstanceBootGroupReadinessRuleResponse; +import org.apache.cloudstack.api.response.InstanceBootGroupResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.bootgroup.readiness.InstanceBootGroupReadinessRule; +import org.apache.cloudstack.vm.bootgroup.readiness.InstanceBootGroupReadinessRuleService; +import org.apache.cloudstack.vm.bootgroup.readiness.ReadinessChecker; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +import com.cloud.api.ApiResponseHelper; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.Project; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.vm.InstanceGroupVMMapVO; +import com.cloud.vm.InstanceGroupVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.InstanceBootGroupDao; +import com.cloud.vm.dao.InstanceBootGroupDetailsDao; +import com.cloud.vm.dao.InstanceBootGroupMemberDao; +import com.cloud.vm.dao.InstanceBootGroupReadinessCheckResultDao; +import com.cloud.vm.dao.InstanceBootGroupReadinessRuleDao; +import com.cloud.vm.dao.InstanceBootGroupReadinessRuleDetailsDao; +import com.cloud.vm.dao.InstanceGroupDao; +import com.cloud.vm.dao.InstanceGroupVMMapDao; +import com.cloud.vm.dao.UserVmDao; + +/** + * API-facing half of the Instance Boot Group feature: ACL, param validation, response building, + * command registration. Delegates orchestration/hypervisor work to {@link InstanceBootGroupManager} + * and membership eligibility checks to {@link InstanceBootGroupMembershipGuard}. + */ +@Component +public class InstanceBootGroupApiServiceImpl implements InstanceBootGroupService, PluggableService { + + @Inject + private InstanceBootGroupDao instanceBootGroupDao; + + @Inject + private InstanceBootGroupJoinDao instanceBootGroupJoinDao; + + @Inject + private InstanceBootGroupMemberDao instanceBootGroupMemberDao; + + @Inject + private AccountManager accountManager; + + @Inject + private UserVmDao userVmDao; + + @Inject + private InstanceGroupDao instanceGroupDao; + + @Inject + private InstanceBootGroupManager instanceBootGroupManager; + + @Inject + private InstanceBootGroupMembershipGuard instanceBootGroupMembershipGuard; + + @Inject + private InstanceBootGroupReadinessRuleService instanceBootGroupReadinessRuleService; + + @Inject + private InstanceBootGroupReadinessRuleDao instanceBootGroupReadinessRuleDao; + + @Inject + private InstanceBootGroupReadinessRuleDetailsDao instanceBootGroupReadinessRuleDetailsDao; + + @Inject + private InstanceBootGroupReadinessCheckResultDao instanceBootGroupReadinessCheckResultDao; + + @Inject + private InstanceBootGroupDetailsDao instanceBootGroupDetailsDao; + + @Inject + private InstanceGroupVMMapDao instanceGroupVMMapDao; + + @NotNull + protected InstanceBootGroupVO getGroupAndCheckAccess(long id) { + InstanceBootGroupVO group = instanceBootGroupDao.findById(id); + if (group == null) { + throw new InvalidParameterValueException("Unable to find instance boot group with ID: " + id); + } + Account caller = CallContext.current().getCallingAccount(); + accountManager.checkAccess(caller, null, true, group); + return group; + } + + protected InstanceBootGroupResponse createInstanceBootGroupResponse(InstanceBootGroupJoinVO bootGroup) { + InstanceBootGroupResponse response = new InstanceBootGroupResponse(); + response.setId(bootGroup.getUuid()); + response.setName(bootGroup.getName()); + response.setDescription(bootGroup.getDescription()); + response.setCreated(bootGroup.getCreated()); + ApiResponseHelper.populateOwner(response, bootGroup); + + String timeoutOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key()); + response.setReadinessAttemptTimeoutSeconds(timeoutOverride != null ? Long.parseLong(timeoutOverride) : InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.value()); + String maxRetryOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key()); + response.setReadinessMaxRetryAttempts(maxRetryOverride != null ? Long.parseLong(maxRetryOverride) : InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.value()); + String rebootOnRetryOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key()); + response.setReadinessRebootOnRetry(rebootOnRetryOverride != null ? Boolean.parseBoolean(rebootOnRetryOverride) : InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.value()); + String initialDelayOverride = instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key()); + response.setReadinessInitialDelaySeconds(initialDelayOverride != null ? Long.parseLong(initialDelayOverride) : InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.value()); + + response.setObjectName("instancebootgroup"); + return response; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_CREATE, eventDescription = "creating Instance Boot Group") + public InstanceBootGroup createInstanceBootGroup(CreateInstanceBootGroupCmd cmd) { + Account caller = CallContext.current().getCallingAccount(); + Account owner = accountManager.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); + + if (instanceBootGroupDao.isNameInUse(owner.getId(), cmd.getName())) { + throw new InvalidParameterValueException("An instance boot group with name '" + cmd.getName() + "' already exists in this account"); + } + + return Transaction.execute((TransactionCallback<InstanceBootGroupVO>) status -> { + InstanceBootGroupVO group = new InstanceBootGroupVO(cmd.getName(), cmd.getDescription(), owner.getId(), owner.getDomainId()); + group = instanceBootGroupDao.persist(group); + CallContext.current().setEventResourceId(group.getId()); + + if (cmd.getReadinessAttemptTimeoutSeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key(), cmd.getReadinessAttemptTimeoutSeconds()); + } + if (cmd.getReadinessMaxRetryAttempts() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key(), cmd.getReadinessMaxRetryAttempts()); + } + if (cmd.getReadinessRebootOnRetry() != null) { + instanceBootGroupDetailsDao.setDetail(group.getId(), InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key(), String.valueOf(cmd.getReadinessRebootOnRetry())); + } + if (cmd.getReadinessInitialDelaySeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key(), cmd.getReadinessInitialDelaySeconds()); + } + + return group; + }); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_DELETE, eventDescription = "deleting Instance Boot Group") + public boolean deleteInstanceBootGroup(DeleteInstanceBootGroupCmd cmd) { + InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId()); + return Transaction.execute((TransactionCallback<Boolean>) status -> { + instanceBootGroupMemberDao.deleteByBootGroupId(group.getId()); + instanceBootGroupDao.remove(group.getId()); + return true; + }); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_UPDATE, eventDescription = "updating Instance Boot Group") + public InstanceBootGroup updateInstanceBootGroup(UpdateInstanceBootGroupCmd cmd) { + InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId()); + + if (cmd.getName() != null && !Objects.equals(cmd.getName(), group.getName())) { + Account owner = accountManager.getAccount(group.getAccountId()); + if (instanceBootGroupDao.isNameInUse(owner.getId(), cmd.getName())) { + throw new InvalidParameterValueException("An instance boot group with name '" + cmd.getName() + "' already exists in this account"); + } + group.setName(cmd.getName()); + } + if (cmd.getDescription() != null) { + group.setDescription(cmd.getDescription()); + } + + return Transaction.execute((TransactionCallback<InstanceBootGroupVO>) status -> { + if (cmd.getReadinessAttemptTimeoutSeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key(), cmd.getReadinessAttemptTimeoutSeconds()); + } + if (cmd.getReadinessMaxRetryAttempts() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key(), cmd.getReadinessMaxRetryAttempts()); + } + if (cmd.getReadinessRebootOnRetry() != null) { + instanceBootGroupDetailsDao.setDetail(group.getId(), InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key(), String.valueOf(cmd.getReadinessRebootOnRetry())); + } + if (cmd.getReadinessInitialDelaySeconds() != null) { + setOrClearOverride(group.getId(), InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key(), cmd.getReadinessInitialDelaySeconds()); + } + + instanceBootGroupDao.update(group.getId(), group); + return instanceBootGroupDao.findById(group.getId()); + }); + } + + private void setOrClearOverride(long bootGroupId, String key, long value) { + if (value < 0) { + instanceBootGroupDetailsDao.setDetail(bootGroupId, key, null); + } else { + instanceBootGroupDetailsDao.setDetail(bootGroupId, key, String.valueOf(value)); + } + } + + @Override + public ListResponse<InstanceBootGroupResponse> listInstanceBootGroups(ListInstanceBootGroupsCmd cmd) { + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + final Long id = cmd.getId(); + final String keyword = cmd.getKeyword(); + + List<InstanceBootGroupResponse> responsesList = new ArrayList<>(); + List<Long> permittedAccounts = new ArrayList<>(); + Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = + new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); + accountManager.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), + permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); + Long domainId = domainIdRecursiveListProject.first(); + Boolean isRecursive = domainIdRecursiveListProject.second(); + Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + + Filter searchFilter = new Filter(InstanceBootGroupJoinVO.class, "id", true, cmd.getStartIndex(), + cmd.getPageSizeVal()); + SearchBuilder<InstanceBootGroupJoinVO> sb = instanceBootGroupJoinDao.createSearchBuilder(); + accountManager.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, + listProjectResourcesCriteria); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE); + SearchCriteria<InstanceBootGroupJoinVO> sc = sb.create(); + accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, + listProjectResourcesCriteria); + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + if (id != null) { + sc.setParameters("id", id); + } + Pair<List<InstanceBootGroupJoinVO>, Integer> bootGroupsAndCount = instanceBootGroupJoinDao.searchAndCount(sc, searchFilter); + for (InstanceBootGroupJoinVO bootGroup : bootGroupsAndCount.first()) { + InstanceBootGroupResponse response = createInstanceBootGroupResponse(bootGroup); + responsesList.add(response); + } + ListResponse<InstanceBootGroupResponse> response = new ListResponse<>(); + response.setResponses(responsesList, bootGroupsAndCount.second()); + return response; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_MEMBER_ADD, eventDescription = "adding Instance Boot Group member") + public InstanceBootGroupMember addMemberToInstanceBootGroup(AddMemberToInstanceBootGroupCmd cmd) { + InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId()); + + if (cmd.getOrder() < 0) { + throw new InvalidParameterValueException("Order value must be 0 or greater"); + } + + InstanceBootGroupMember.MemberType memberType; + long memberId; + + validateEitherVirtualMachineIdOrInstanceGroupIdParam(cmd.getVirtualMachineId(), cmd.getInstanceGroupId()); + + if (cmd.getVirtualMachineId() != null) { + UserVm vm = getValidatedVmForAddMember(group, cmd.getVirtualMachineId()); + memberType = InstanceBootGroupMember.MemberType.VirtualMachine; + memberId = vm.getId(); + } else { + InstanceGroupVO instanceGroup = getValidatedInstanceGroupAddMember(group, cmd.getInstanceGroupId()); + memberType = InstanceBootGroupMember.MemberType.InstanceGroup; + memberId = instanceGroup.getId(); + } + + if (instanceBootGroupMemberDao.findByMember(memberType, memberId) != null) { + throw new InvalidParameterValueException(String.format("This %s already belongs to an instance boot group", memberType.name())); + } + + List<InstanceBootGroupMemberVO> siblings = instanceBootGroupMemberDao.listByBootGroupId(group.getId()); Review Comment: done -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
