Skip to content

Commit 6af1c25

Browse files
committed
Merge remote-tracking branch 'apache/4.19'
2 parents a31449b + a0e592e commit 6af1c25

File tree

23 files changed

+173
-152
lines changed

23 files changed

+173
-152
lines changed

api/src/main/java/com/cloud/server/ManagementService.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,11 @@ public interface ManagementService {
449449
* this method removes the child storage pools and adds the corresponding parent datastore cluster for API response listing
450450
*
451451
* @param Long volumeId
452+
* @param String keyword if passed, will only return storage pools that contain this keyword in the name
452453
* @return Pair<List<? extends StoragePool>, List<? extends StoragePool>> List of storage pools in cluster and list
453454
* of pools with enough capacity.
454455
*/
455-
Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolume(Long volumeId);
456+
Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolume(Long volumeId, String keyword);
456457

457458
Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForSystemMigrationOfVolume(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck);
458459

api/src/main/java/com/cloud/vm/NicProfile.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,6 @@ public void deallocate() {
442442

443443
@Override
444444
public String toString() {
445-
return String.format("NicProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "vmId", "reservationId", "iPv4Address", "broadcastUri"));
445+
return String.format("NicProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "vmId", "deviceId", "broadcastUri", "reservationId", "iPv4Address"));
446446
}
447447
}

api/src/main/java/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public ApiCommandResourceType getApiResourceType() {
6565

6666
@Override
6767
public void execute() {
68-
Pair<List<? extends StoragePool>, List<? extends StoragePool>> pools = _mgr.listStoragePoolsForMigrationOfVolume(getId());
68+
Pair<List<? extends StoragePool>, List<? extends StoragePool>> pools = _mgr.listStoragePoolsForMigrationOfVolume(getId(), getKeyword());
6969
ListResponse<StoragePoolResponse> response = new ListResponse<StoragePoolResponse>();
7070
List<StoragePoolResponse> poolResponses = new ArrayList<StoragePoolResponse>();
7171

@@ -85,7 +85,8 @@ public void execute() {
8585
poolResponses.add(poolResponse);
8686
}
8787
sortPoolsBySuitabilityAndName(poolResponses);
88-
response.setResponses(poolResponses);
88+
List<StoragePoolResponse> pagingList = com.cloud.utils.StringUtils.applyPagination(poolResponses, this.getStartIndex(), this.getPageSizeVal());
89+
response.setResponses(pagingList, poolResponses.size());
8990
response.setResponseName(getCommandName());
9091
this.setResponseObject(response);
9192
}

core/src/main/java/com/cloud/serializer/GsonHelper.java

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class GsonHelper {
5454
GsonBuilder LOGGERBuilder = new GsonBuilder();
5555
LOGGERBuilder.disableHtmlEscaping();
5656
LOGGERBuilder.setExclusionStrategies(new LoggingExclusionStrategy(LOGGER));
57+
LOGGERBuilder.serializeSpecialFloatingPointValues();
58+
// maybe add LOGGERBuilder.serializeNulls(); as well?
5759
s_gogger = setDefaultGsonConfig(LOGGERBuilder);
5860
LOGGER.info("Default Builder inited.");
5961
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java

+3
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ public interface StoragePoolAllocator extends Adapter {
5252
* avoid
5353
* @param int returnUpTo (use -1 to return all possible pools)
5454
* @param boolean bypassStorageTypeCheck allows bypassing useLocalStorage check for provided DiskProfile when true
55+
* @param String keyword if passed, will only return storage pools that contain this keyword in the name
5556
* @return List<StoragePool> List of storage pools that are suitable for the
5657
* VM
5758
**/
59+
List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword);
60+
5861
List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck);
5962

6063

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -2308,12 +2308,12 @@ public void releaseNic(final VirtualMachineProfile vmProfile, final Nic nic) thr
23082308

23092309
@DB
23102310
protected void releaseNic(final VirtualMachineProfile vmProfile, final long nicId) throws ConcurrentOperationException, ResourceUnavailableException {
2311-
final Pair<Network, NicProfile> networkToRelease = Transaction.execute(new TransactionCallback<Pair<Network, NicProfile>>() {
2311+
final Pair<Network, NicProfile> networkToRelease = Transaction.execute(new TransactionCallback<>() {
23122312
@Override
23132313
public Pair<Network, NicProfile> doInTransaction(final TransactionStatus status) {
23142314
final NicVO nic = _nicDao.lockRow(nicId, true);
23152315
if (nic == null) {
2316-
throw new ConcurrentOperationException("Unable to acquire lock on nic " + nic);
2316+
throw new ConcurrentOperationException(String.format("Unable to acquire lock on nic id=%d", nicId));
23172317
}
23182318

23192319
final Nic.State originalState = nic.getState();
@@ -2327,6 +2327,9 @@ public Pair<Network, NicProfile> doInTransaction(final TransactionStatus status)
23272327
final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, _networkModel
23282328
.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network));
23292329
if (guru.release(profile, vmProfile, nic.getReservationId())) {
2330+
if (logger.isDebugEnabled()) {
2331+
logger.debug(String.format("The nic %s on %s was released according to %s by guru %s, now updating record.", nic, profile, vmProfile, guru));
2332+
}
23302333
applyProfileToNicForRelease(nic, profile);
23312334
nic.setState(Nic.State.Allocated);
23322335
if (originalState == Nic.State.Reserved) {
@@ -2336,7 +2339,7 @@ public Pair<Network, NicProfile> doInTransaction(final TransactionStatus status)
23362339
}
23372340
}
23382341
// Perform release on network elements
2339-
return new Pair<Network, NicProfile>(network, profile);
2342+
return new Pair<>(network, profile);
23402343
} else {
23412344
nic.setState(Nic.State.Allocated);
23422345
updateNic(nic, network.getId(), -1);
@@ -2433,7 +2436,7 @@ protected void removeNic(final VirtualMachineProfile vm, final NicVO nic) {
24332436
for (final NetworkElement element : networkElements) {
24342437
if (providersToImplement.contains(element.getProvider())) {
24352438
if (logger.isDebugEnabled()) {
2436-
logger.debug("Asking " + element.getName() + " to release " + nic);
2439+
logger.debug(String.format("Asking %s to release %s, according to the reservation strategy %s", element.getName(), nic, nic.getReservationStrategy()));
24372440
}
24382441
try {
24392442
element.release(network, profile, vm, null);

engine/schema/src/main/java/com/cloud/vm/NicVO.java

+2-11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import javax.persistence.Table;
3131
import javax.persistence.Transient;
3232

33+
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
3334
import org.apache.commons.lang3.builder.EqualsBuilder;
3435
import org.apache.commons.lang3.builder.HashCodeBuilder;
3536

@@ -329,17 +330,7 @@ public void setCreated(Date created) {
329330

330331
@Override
331332
public String toString() {
332-
return new StringBuilder("Nic[").append(id)
333-
.append("-")
334-
.append(instanceId)
335-
.append("-")
336-
.append(deviceId)
337-
.append("-")
338-
.append(reservationId)
339-
.append("-")
340-
.append(iPv4Address)
341-
.append("]")
342-
.toString();
333+
return String.format("Nic %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "instanceId", "deviceId", "broadcastUri", "reservationId", "iPv4Address"));
343334
}
344335

345336
@Override

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public interface PrimaryDataStoreDao extends GenericDao<StoragePoolVO, Long> {
4040
*/
4141
List<StoragePoolVO> listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope);
4242

43+
List<StoragePoolVO> listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope, String keyword);
44+
4345
/**
4446
* Set capacity of storage pool in bytes
4547
* @param id pool id.
@@ -115,15 +117,19 @@ public interface PrimaryDataStoreDao extends GenericDao<StoragePoolVO, Long> {
115117

116118
List<StoragePoolVO> findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule);
117119

120+
List<StoragePoolVO> findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, String keyword);
121+
118122
List<StoragePoolVO> findZoneWideStoragePoolsByTags(long dcId, String[] tags, boolean validateTagRule);
119123

120124
List<StoragePoolVO> findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType);
121125

126+
List<StoragePoolVO> findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword);
127+
122128
List<StoragePoolVO> findLocalStoragePoolsByHostAndTags(long hostId, String[] tags);
123129

124130
List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String path);
125131

126-
List<StoragePoolVO> findPoolsInClusters(List<Long> clusterIds);
132+
List<StoragePoolVO> findPoolsInClusters(List<Long> clusterIds, String keyword);
127133

128134
void deletePoolTags(long poolId);
129135

engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ public List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String
244244

245245
@Override
246246
public List<StoragePoolVO> listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope) {
247+
return listBy(datacenterId, podId, clusterId, scope, null);
248+
}
249+
250+
@Override
251+
public List<StoragePoolVO> listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope, String keyword) {
247252
SearchCriteria<StoragePoolVO> sc = null;
248253
if (clusterId != null) {
249254
sc = DcPodSearch.create();
@@ -255,6 +260,9 @@ public List<StoragePoolVO> listBy(long datacenterId, Long podId, Long clusterId,
255260
sc.setParameters("datacenterId", datacenterId);
256261
sc.setParameters("podId", podId);
257262
sc.setParameters("status", Status.Up);
263+
if (keyword != null) {
264+
sc.addAnd("name", Op.LIKE, "%" + keyword + "%");
265+
}
258266
if (scope != null) {
259267
sc.setParameters("scope", scope);
260268
}
@@ -444,9 +452,14 @@ public List<StoragePoolVO> findDisabledPoolsByScope(long dcId, Long podId, Long
444452

445453
@Override
446454
public List<StoragePoolVO> findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule) {
455+
return findLocalStoragePoolsByTags(dcId, podId, clusterId, tags, validateTagRule, null);
456+
}
457+
458+
@Override
459+
public List<StoragePoolVO> findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, String keyword) {
447460
List<StoragePoolVO> storagePools = null;
448461
if (tags == null || tags.length == 0) {
449-
storagePools = listBy(dcId, podId, clusterId, ScopeType.HOST);
462+
storagePools = listBy(dcId, podId, clusterId, ScopeType.HOST, keyword);
450463

451464
if (validateTagRule) {
452465
storagePools = getPoolsWithoutTagRule(storagePools);
@@ -583,11 +596,19 @@ public List<StoragePoolVO> listPoolsByCluster(long clusterId) {
583596

584597
@Override
585598
public List<StoragePoolVO> findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType) {
599+
return findZoneWideStoragePoolsByHypervisor(dataCenterId, hypervisorType, null);
600+
}
601+
602+
@Override
603+
public List<StoragePoolVO> findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword) {
586604
QueryBuilder<StoragePoolVO> sc = QueryBuilder.create(StoragePoolVO.class);
587605
sc.and(sc.entity().getDataCenterId(), Op.EQ, dataCenterId);
588606
sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
589607
sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE);
590608
sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType);
609+
if (keyword != null) {
610+
sc.and(sc.entity().getName(), Op.LIKE, "%" + keyword + "%");
611+
}
591612
return sc.list();
592613
}
593614

@@ -612,10 +633,13 @@ public Integer countAll() {
612633
}
613634

614635
@Override
615-
public List<StoragePoolVO> findPoolsInClusters(List<Long> clusterIds) {
636+
public List<StoragePoolVO> findPoolsInClusters(List<Long> clusterIds, String keyword) {
616637
SearchCriteria<StoragePoolVO> sc = ClustersSearch.create();
617638
sc.setParameters("clusterIds", clusterIds.toArray());
618639
sc.setParameters("status", StoragePoolStatus.Up);
640+
if (keyword != null) {
641+
sc.addAnd("name", Op.LIKE, "%" + keyword + "%");
642+
}
619643
return listBy(sc);
620644
}
621645

engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,20 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
104104
return false;
105105
}
106106

107-
protected abstract List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck);
107+
protected abstract List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword);
108108

109109
@Override
110110
public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) {
111-
return allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo, false);
111+
return allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo, false, null);
112112
}
113113

114114
@Override
115115
public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
116-
List<StoragePool> pools = select(dskCh, vmProfile, plan, avoid, returnUpTo, bypassStorageTypeCheck);
116+
return allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo, bypassStorageTypeCheck, null);
117+
}
118+
119+
public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
120+
List<StoragePool> pools = select(dskCh, vmProfile, plan, avoid, returnUpTo, bypassStorageTypeCheck, keyword);
117121
return reorderPools(pools, vmProfile, plan, dskCh);
118122
}
119123

engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat
4444
DiskOfferingDao _diskOfferingDao;
4545

4646
@Override
47-
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
47+
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
4848
logStartOfSearch(dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck);
4949

5050
if (!bypassStorageTypeCheck && dskCh.useLocalStorage()) {

engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/GarbageCollectingStoragePoolAllocator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class GarbageCollectingStoragePoolAllocator extends AbstractStoragePoolAl
4545
boolean _storagePoolCleanupEnabled;
4646

4747
@Override
48-
public List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
48+
public List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
4949
logStartOfSearch(dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck);
5050
if (!_storagePoolCleanupEnabled) {
5151
logger.debug("Storage pool cleanup is not enabled, so GarbageCollectingStoragePoolAllocator is being skipped.");

engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class LocalStoragePoolAllocator extends AbstractStoragePoolAllocator {
5858
ConfigurationDao _configDao;
5959

6060
@Override
61-
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
61+
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
6262
logStartOfSearch(dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck);
6363

6464
if (!bypassStorageTypeCheck && !dskCh.useLocalStorage()) {
@@ -99,7 +99,7 @@ protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmPr
9999
return null;
100100
}
101101
List<StoragePoolVO> availablePools =
102-
storagePoolDao.findLocalStoragePoolsByTags(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId(), dskCh.getTags(), true);
102+
storagePoolDao.findLocalStoragePoolsByTags(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId(), dskCh.getTags(), true, keyword);
103103
availablePools.addAll(storagePoolJoinDao.findStoragePoolByScopeAndRuleTags(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId(), ScopeType.HOST, List.of(dskCh.getTags())));
104104
for (StoragePoolVO pool : availablePools) {
105105
if (suitablePools.size() == returnUpTo) {

engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator {
4848
private CapacityDao capacityDao;
4949

5050
@Override
51-
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
51+
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
5252
logStartOfSearch(dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck);
5353

5454
if (!bypassStorageTypeCheck && dskCh.useLocalStorage()) {

engine/storage/src/test/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocatorTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public void reorderRandomPools() {
137137
class MockStorapoolAllocater extends AbstractStoragePoolAllocator {
138138

139139
@Override
140-
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, DeploymentPlanner.ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
140+
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, DeploymentPlanner.ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
141141
return null;
142142
}
143143
}

plugins/storage-allocators/random/src/main/java/org/apache/cloudstack/storage/allocator/RandomStoragePoolAllocator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
public class RandomStoragePoolAllocator extends AbstractStoragePoolAllocator {
3434

3535
@Override
36-
public List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) {
36+
public List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck, String keyword) {
3737
logStartOfSearch(dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck);
3838

3939
List<StoragePool> suitablePools = new ArrayList<StoragePool>();

0 commit comments

Comments
 (0)