Skip to content

Add support for disabling serivce / system and disk offerings #9546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.offering;

import static com.cloud.offering.DiskOffering.State.Active;

import com.cloud.offering.DiskOffering;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
Expand All @@ -29,7 +28,6 @@
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;

import com.cloud.offering.DiskOffering.State;

Expand Down Expand Up @@ -111,14 +109,7 @@
}

public State getState() {
if (StringUtils.isBlank(diskOfferingState)) {
return Active;
}
State state = EnumUtils.getEnumIgnoreCase(State.class, diskOfferingState);
if (!diskOfferingState.equalsIgnoreCase("all") && state == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these checks not relevant anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just made this uniform to how it works with listing network and vpc offerings ; wherein when the state isn't passed, we list all offerings - so we do not explicitly need "all"

throw new IllegalArgumentException("Invalid state value: " + diskOfferingState);
}
return state;
return EnumUtils.getEnumIgnoreCase(DiskOffering.State.class, diskOfferingState);

Check warning on line 112 in api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java

View check run for this annotation

Codecov / codecov/patch

api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java#L112

Added line #L112 was not covered by tests
}

public Long getVirtualMachineId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
// under the License.
package org.apache.cloudstack.api.command.user.offering;

import static com.cloud.offering.ServiceOffering.State.Active;

import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
Expand All @@ -28,7 +26,6 @@
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;

import com.cloud.offering.ServiceOffering.State;

Expand Down Expand Up @@ -157,14 +154,7 @@
}

public State getState() {
if (StringUtils.isBlank(serviceOfferingState)) {
return Active;
}
State state = EnumUtils.getEnumIgnoreCase(State.class, serviceOfferingState);
if (!serviceOfferingState.equalsIgnoreCase("all") && state == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just made this uniform to how it works with listing network and vpc offerings ; wherein when the state isn't passed, we list all offerings - so we do not explicitly need "all"

throw new IllegalArgumentException("Invalid state value: " + serviceOfferingState);
}
return state;
return EnumUtils.getEnumIgnoreCase(State.class, serviceOfferingState);

Check warning on line 157 in api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java

View check run for this annotation

Codecov / codecov/patch

api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java#L157

Added line #L157 was not covered by tests
}

public Long getTemplateId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,6 @@ List<VMInstanceVO> searchRemovedByRemoveDate(final Date startDate, final Date en
List<Long> skippedVmIds);

Pair<List<VMInstanceVO>, Integer> listByVmsNotInClusterUsingPool(long clusterId, long poolId);

List<VMInstanceVO> listByOfferingId(long offeringId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
protected SearchBuilder<VMInstanceVO> StartingWithNoHostSearch;
protected SearchBuilder<VMInstanceVO> NotMigratingSearch;
protected SearchBuilder<VMInstanceVO> BackupSearch;
protected SearchBuilder<VMInstanceVO> ServiceOfferingSearch;
protected SearchBuilder<VMInstanceVO> LastHostAndStatesSearch;
protected SearchBuilder<VMInstanceVO> VmsNotInClusterUsingPool;

Expand Down Expand Up @@ -325,6 +326,10 @@
VmsNotInClusterUsingPool.join("hostSearch2", hostSearch2, hostSearch2.entity().getId(), VmsNotInClusterUsingPool.entity().getHostId(), JoinType.INNER);
VmsNotInClusterUsingPool.and("vmStates", VmsNotInClusterUsingPool.entity().getState(), Op.IN);
VmsNotInClusterUsingPool.done();

ServiceOfferingSearch = createSearchBuilder();
ServiceOfferingSearch.and("serviceOfferingId", ServiceOfferingSearch.entity().getServiceOfferingId(), Op.EQ);
BackupSearch.done();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be ServiceOfferingSearch.done here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right

}

@Override
Expand Down Expand Up @@ -1069,4 +1074,11 @@
List<VMInstanceVO> uniqueVms = vms.stream().distinct().collect(Collectors.toList());
return new Pair<>(uniqueVms, uniqueVms.size());
}

@Override
public List<VMInstanceVO> listByOfferingId(long offeringId) {
SearchCriteria<VMInstanceVO> sc = ServiceOfferingSearch.create();
sc.setParameters("serviceOfferingId", offeringId);
return search(sc, null);
}

Check warning on line 1083 in engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java

View check run for this annotation

Codecov / codecov/patch

engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java#L1079-L1083

Added lines #L1079 - L1083 were not covered by tests
}
11 changes: 7 additions & 4 deletions server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3430,7 +3430,7 @@
diskOfferingSearch.and("computeOnly", diskOfferingSearch.entity().isComputeOnly(), Op.EQ);

if (state != null) {
diskOfferingSearch.and("state", diskOfferingSearch.entity().getState(), Op.EQ);
diskOfferingSearch.and("state", diskOfferingSearch.entity().getState(), Op.IN);

Check warning on line 3433 in server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java#L3433

Added line #L3433 was not covered by tests
}

// Keeping this logic consistent with domain specific zones
Expand Down Expand Up @@ -3547,6 +3547,8 @@

if (state != null) {
sc.setParameters("state", state);
} else {
sc.setParameters("state", Arrays.asList(ServiceOffering.State.Active, ServiceOffering.State.Inactive));

Check warning on line 3551 in server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java#L3551

Added line #L3551 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case we don't specify what offering is required , this would list offerings in both active and inactive states

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do not set the state condition (AKA where state in x), both active and inactive will be listed.

}

if (keyword != null) {
Expand Down Expand Up @@ -3756,7 +3758,7 @@
serviceOfferingSearch.select(null, Func.DISTINCT, serviceOfferingSearch.entity().getId()); // select distinct

if (state != null) {
serviceOfferingSearch.and("state", serviceOfferingSearch.entity().getState(), Op.EQ);
serviceOfferingSearch.and("state", serviceOfferingSearch.entity().getState(), Op.IN);

Check warning on line 3761 in server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java#L3761

Added line #L3761 was not covered by tests
}

if (vmId != null) {
Expand Down Expand Up @@ -3913,8 +3915,7 @@
}

serviceOfferingSearch.join("diskOfferingSearch", diskOfferingSearch, JoinBuilder.JoinType.INNER, JoinBuilder.JoinCondition.AND,
serviceOfferingSearch.entity().getDiskOfferingId(), diskOfferingSearch.entity().getId(),
serviceOfferingSearch.entity().setString("Active"), diskOfferingSearch.entity().getState());
serviceOfferingSearch.entity().getDiskOfferingId(), diskOfferingSearch.entity().getId());

Check warning on line 3918 in server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java#L3918

Added line #L3918 was not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a query that is executed if vmid != null? if so, should we list inactive offerings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this PR aims to support disabling service offerings - to make it similar to the way network offerings work, it would make sense to list active and inactive offerings

Copy link
Contributor

@JoaoJandre JoaoJandre Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is too long (500+ lines...) for me to judge what it is exactly doing and where it is being done.
I just want to make sure that when creating VMs we don't list inactive offerings. If this change does not cause that, then I'm ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Al, that's a fair point.. Probably reverting to it listing Active by default would be a better option. Thanks.

}

if (cpuNumber != null) {
Expand Down Expand Up @@ -4043,6 +4044,8 @@
SearchCriteria<ServiceOfferingVO> sc = serviceOfferingSearch.create();
if (state != null) {
sc.setParameters("state", state);
} else {
sc.setParameters("state", Arrays.asList(ServiceOffering.State.Active, ServiceOffering.State.Inactive));

Check warning on line 4048 in server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java#L4048

Added line #L4048 was not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, if you do not set this parameter, both active and inactive offerings will be queried.

}

if (vmId != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import com.cloud.host.HostTagVO;
import com.cloud.storage.StoragePoolTagVO;
import com.cloud.storage.VolumeApiServiceImpl;
import com.cloud.vm.VMInstanceVO;
import com.googlecode.ipv6.IPv6Address;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.affinity.AffinityGroup;
Expand Down Expand Up @@ -4324,8 +4325,11 @@
}

annotationDao.removeByEntityType(AnnotationService.EntityType.DISK_OFFERING.name(), offering.getUuid());
offering.setState(DiskOffering.State.Inactive);
if (_diskOfferingDao.update(offering.getId(), offering)) {
List<VolumeVO> volumesUsingOffering = _volumeDao.findByDiskOfferingId(diskOfferingId);

Check warning on line 4328 in server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java#L4328

Added line #L4328 was not covered by tests
if (!volumesUsingOffering.isEmpty()) {
throw new InvalidParameterValueException(String.format("Unable to delete disk offering: %s [%s] because there are volumes using it", offering.getUuid(), offering.getName()));

Check warning on line 4330 in server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java#L4330

Added line #L4330 was not covered by tests
}
Comment on lines +4371 to +4374
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this related to the main change? Why should we start blocking this now?

if (_diskOfferingDao.remove(offering.getId())) {
CallContext.current().setEventDetails("Disk offering id=" + diskOfferingId);
return true;
} else {
Expand Down Expand Up @@ -4397,15 +4401,17 @@
throw new InvalidParameterValueException(String.format("Unable to delete service offering: %s by user: %s because it is not root-admin or domain-admin", offering.getUuid(), user.getUuid()));
}

List<VMInstanceVO> vmsUsingOffering = _vmInstanceDao.listByOfferingId(offeringId);

Check warning on line 4404 in server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java#L4404

Added line #L4404 was not covered by tests
if (!vmsUsingOffering.isEmpty()) {
throw new CloudRuntimeException(String.format("Unable to delete service offering %s as it is in use", offering.getUuid()));

Check warning on line 4406 in server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java#L4406

Added line #L4406 was not covered by tests
}
Comment on lines +4447 to +4450
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this related to the main change? Why should we start blocking this now?

annotationDao.removeByEntityType(AnnotationService.EntityType.SERVICE_OFFERING.name(), offering.getUuid());
if (diskOffering.isComputeOnly()) {
diskOffering.setState(DiskOffering.State.Inactive);
if (!_diskOfferingDao.update(diskOffering.getId(), diskOffering)) {
if (!_diskOfferingDao.remove(diskOffering.getId())) {
throw new CloudRuntimeException(String.format("Unable to delete disk offering %s mapped to the service offering %s", diskOffering.getUuid(), offering.getUuid()));
}
}
offering.setState(ServiceOffering.State.Inactive);
if (_serviceOfferingDao.update(offeringId, offering)) {
if (_serviceOfferingDao.remove(offeringId)) {
CallContext.current().setEventDetails("Service offering id=" + offeringId);
return true;
} else {
Expand Down
2 changes: 1 addition & 1 deletion tools/marvin/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
raise RuntimeError("python setuptools is required to build Marvin")


VERSION = "4.20.0.0-SNAPSHOT"
VERSION = "4.20.0.0"

setup(name="Marvin",
version=VERSION,
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"label.action.delete.primary.storage": "Delete primary storage",
"label.action.delete.secondary.storage": "Delete secondary storage",
"label.action.delete.security.group": "Delete security group",
"label.action.delete.service.offering": "Delete compute offering",
"label.action.delete.snapshot": "Delete Snapshot",
"label.action.delete.template": "Delete Template",
"label.action.delete.tungsten.router.table": "Remove Tungsten Fabric route table from Network",
Expand Down
68 changes: 59 additions & 9 deletions ui/src/config/section/offering.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export default {
permission: ['listServiceOfferings'],
searchFilters: ['name', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory'],
params: () => {
var params = {}
var params = { state: 'all' }
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
params = { isrecursive: 'true' }
params.isrecursive = true
}
return params
},
Expand Down Expand Up @@ -121,16 +121,33 @@ export default {
popup: true,
show: (record) => { return record.state !== 'Active' },
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Active' } }) }
}, {
api: 'deleteServiceOffering',
},
{
api: 'updateServiceOffering',
icon: 'pause-circle-outlined',
label: 'label.action.disable.service.offering',
message: 'message.action.disable.service.offering',
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
dataView: true,
args: ['state'],
mapping: {
state: {
value: (record) => { return 'Inactive' }
}
},
groupAction: true,
popup: true,
show: (record) => { return record.state === 'Active' },
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Active' } }) }
},
{
api: 'deleteServiceOffering',
icon: 'delete-outlined',
label: 'label.action.delete.service.offering',
message: 'message.action.delete.service.offering',
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
dataView: true,
groupAction: true,
popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
}]
},
Expand Down Expand Up @@ -198,16 +215,33 @@ export default {
show: (record) => { return record.state !== 'Active' },
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Active' } }) }
}, {
api: 'deleteServiceOffering',
api: 'updateServiceOffering',
icon: 'pause-circle-outlined',
label: 'label.action.disable.system.service.offering',
message: 'message.action.disable.system.service.offering',
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
dataView: true,
params: { issystem: 'true' },
args: ['state'],
mapping: {
state: {
value: (record) => { return 'Inactive' }
}
},
groupAction: true,
popup: true,
show: (record) => { return record.state === 'Active' },
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Active' } }) }
},
{
api: 'deleteServiceOffering',
icon: 'delete-outlined',
label: 'label.action.delete.system.service.offering',
message: 'message.action.delete.system.service.offering',
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
dataView: true,
params: { issystem: 'true' },
groupAction: true,
popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
}]
},
Expand Down Expand Up @@ -301,15 +335,31 @@ export default {
show: (record) => { return record.state !== 'Active' },
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Active' } }) }
}, {
api: 'deleteDiskOffering',
api: 'updateDiskOffering',
icon: 'pause-circle-outlined',
label: 'label.action.disable.disk.offering',
message: 'message.action.disable.disk.offering',
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
dataView: true,
params: { issystem: 'true' },
args: ['state'],
mapping: {
state: {
value: (record) => { return 'Inactive' }
}
},
groupAction: true,
popup: true,
show: (record) => { return record.state === 'Active' },
groupMap: (selection) => { return selection.map(x => { return { id: x, state: 'Active' } }) }
}, {
api: 'deleteDiskOffering',
icon: 'delete-outlined',
label: 'label.action.delete.disk.offering',
message: 'message.action.delete.disk.offering',
docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering',
dataView: true,
groupAction: true,
popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
}]
},
Expand Down
Loading