Changes for implicitly dedicating a resource. It includes a following:

1. A new implicit planner which extends the functionality provided by FirstFitPlanner.
2. Implicit planner can be used in either strict or preferred mode. In strict mode it tries to deploy a vm of a given account on a host on which vms of the account are already running. If no such host is found it'll search for an empty host to service the request. Otherwise the deploy vm request fails.
3. In preferred mode, if a host which is running vms of the account or an empty host isn't found, the planner then tries to deploy on any other host provided it isn't running implicitly dedicated strict vms of any other account.
4. Updated the createServiceOffering api to configure the details for the planner that the service offering is using.
5. Made db changes to store the service offering details for the planner.
6. Unit tests for testing the implicit planner functionality.
7. Marvin test for validating the functionality.
This commit is contained in:
Devdeep Singh 2013-05-17 11:14:25 +05:30
parent caf0dd22b7
commit adbebc1892
23 changed files with 1430 additions and 18 deletions

View File

@ -213,6 +213,13 @@ public interface DeploymentPlanner extends Adapter {
_hostIds.add(hostId);
}
public void addHostList(Collection<Long> hostList) {
if (_hostIds == null) {
_hostIds = new HashSet<Long>();
}
_hostIds.addAll(hostList);
}
public boolean shouldAvoid(Host host) {
if (_dcIds != null && _dcIds.contains(host.getDataCenterId())) {
return true;

View File

@ -312,6 +312,7 @@ public class ApiConstants {
public static final String ACCEPT = "accept";
public static final String SORT_KEY = "sortkey";
public static final String ACCOUNT_DETAILS = "accountdetails";
public static final String SERVICE_OFFERING_DETAILS = "serviceofferingdetails";
public static final String SERVICE_PROVIDER_LIST = "serviceproviderlist";
public static final String SERVICE_CAPABILITY_LIST = "servicecapabilitylist";
public static final String CAN_CHOOSE_SERVICE_CAPABILITY = "canchooseservicecapability";

View File

@ -16,6 +16,9 @@
// under the License.
package org.apache.cloudstack.api.command.admin.offering;
import java.util.Collection;
import java.util.Map;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
@ -87,6 +90,9 @@ public class CreateServiceOfferingCmd extends BaseCmd {
@Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "The deployment planner heuristics used to deploy a VM of this offering. If null, value of global config vm.deployment.planner is used")
private String deploymentPlanner;
@Parameter(name = ApiConstants.SERVICE_OFFERING_DETAILS, type = CommandType.MAP, description = "details for planner, used to store specific parameters")
private Map<String, String> details;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -155,6 +161,16 @@ public class CreateServiceOfferingCmd extends BaseCmd {
return deploymentPlanner;
}
public Map<String, String> getDetails() {
if (details == null || details.isEmpty()) {
return null;
}
Collection<String> paramsCollection = details.values();
Map<String, String> params = (Map<String, String>)(paramsCollection.toArray())[0];
return params;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -131,6 +131,11 @@
<artifactId>cloud-plugin-planner-user-concentrated-pod</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-planner-implicit-dedication</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-host-allocator-random</artifactId>

View File

@ -370,7 +370,7 @@
<bean id="vpnUserDaoImpl" class="com.cloud.network.dao.VpnUserDaoImpl" />
<bean id="applicationLbRuleDaoImpl" class="org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDaoImpl" />
<bean id="networkOfferingDetailsDaoImpl" class="com.cloud.offerings.dao.NetworkOfferingDetailsDaoImpl" />
<bean id="serviceOfferingDetailsDaoImpl" class="com.cloud.service.dao.ServiceOfferingDetailsDaoImpl"/>
<!--
Checkers
@ -547,6 +547,10 @@
<property name="name" value="UserConcentratedPodPlanner"/>
</bean>
<bean id="ImplicitDedicationPlanner" class="com.cloud.deploy.ImplicitDedicationPlanner">
<property name="name" value="ImplicitDedicationPlanner"/>
</bean>
<bean id="clusterBasedAgentLoadBalancerPlanner" class="com.cloud.cluster.agentlb.ClusterBasedAgentLoadBalancerPlanner">
<property name="name" value="ClusterBasedAgentLoadBalancerPlanner"/>
</bean>

View File

@ -156,6 +156,7 @@
<ref bean="FirstFitPlanner" />
<ref bean="UserDispersingPlanner" />
<ref bean="UserConcentratedPodPlanner" />
<ref bean="ImplicitDedicationPlanner" />
<!--
<ref bean="BareMetalPlanner" />
-->

View File

@ -0,0 +1,73 @@
// 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 com.cloud.service;
import org.apache.cloudstack.api.InternalIdentity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="service_offering_details")
public class ServiceOfferingDetailsVO implements InternalIdentity {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private long id;
@Column(name="service_offering_id")
private long serviceOfferingId;
@Column(name="name")
private String name;
@Column(name="value")
private String value;
protected ServiceOfferingDetailsVO() {
}
public ServiceOfferingDetailsVO(long serviceOfferingId, String name, String value) {
this.serviceOfferingId = serviceOfferingId;
this.name = name;
this.value = value;
}
public long getServiceOfferingId() {
return serviceOfferingId;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public long getId() {
return id;
}
}

View File

@ -16,6 +16,8 @@
// under the License.
package com.cloud.service;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@ -71,6 +73,12 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering
@Column(name = "deployment_planner")
private String deploymentPlanner = null;
// This is a delayed load value. If the value is null,
// then this field has not been loaded yet.
// Call service offering dao to load it.
@Transient
Map<String, String> details;
protected ServiceOfferingVO() {
super();
}
@ -225,4 +233,23 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering
return deploymentPlanner;
}
public Map<String, String> getDetails() {
return details;
}
public String getDetail(String name) {
assert (details != null) : "Did you forget to load the details?";
return details != null ? details.get(name) : null;
}
public void setDetail(String name, String value) {
assert (details != null) : "Did you forget to load the details?";
details.put(name, value);
}
public void setDetails(Map<String, String> details) {
this.details = details;
}
}

View File

@ -31,4 +31,6 @@ public interface ServiceOfferingDao extends GenericDao<ServiceOfferingVO, Long>
List<ServiceOfferingVO> findServiceOfferingByDomainId(Long domainId);
List<ServiceOfferingVO> findSystemOffering(Long domainId, Boolean isSystem, String vm_type);
ServiceOfferingVO persistDeafultServiceOffering(ServiceOfferingVO offering);
void loadDetails(ServiceOfferingVO serviceOffering);
void saveDetails(ServiceOfferingVO serviceOffering);
}

View File

@ -18,15 +18,16 @@ package com.cloud.service.dao;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.ejb.Local;
import javax.inject.Inject;
import javax.persistence.EntityExistsException;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
@ -37,6 +38,8 @@ import com.cloud.utils.db.SearchCriteria;
public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Long> implements ServiceOfferingDao {
protected static final Logger s_logger = Logger.getLogger(ServiceOfferingDaoImpl.class);
@Inject protected ServiceOfferingDetailsDao detailsDao;
protected final SearchBuilder<ServiceOfferingVO> UniqueNameSearch;
protected final SearchBuilder<ServiceOfferingVO> ServiceOfferingsByDomainIdSearch;
protected final SearchBuilder<ServiceOfferingVO> SystemServiceOffering;
@ -154,4 +157,18 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Lo
return update(id, offering);
}
@Override
public void loadDetails(ServiceOfferingVO serviceOffering) {
Map<String, String> details = detailsDao.findDetails(serviceOffering.getId());
serviceOffering.setDetails(details);
}
@Override
public void saveDetails(ServiceOfferingVO serviceOffering) {
Map<String, String> details = serviceOffering.getDetails();
if (details != null) {
detailsDao.persist(serviceOffering.getId(), details);
}
}
}

View File

@ -0,0 +1,29 @@
// 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 com.cloud.service.dao;
import java.util.Map;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.utils.db.GenericDao;
public interface ServiceOfferingDetailsDao extends GenericDao<ServiceOfferingDetailsVO, Long> {
Map<String, String> findDetails(long serviceOfferingId);
void persist(long serviceOfferingId, Map<String, String> details);
ServiceOfferingDetailsVO findDetail(long serviceOfferingId, String name);
void deleteDetails(long serviceOfferingId);
}

View File

@ -0,0 +1,98 @@
// 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 com.cloud.service.dao;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.Local;
import org.springframework.stereotype.Component;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
@Component
@Local(value=ServiceOfferingDetailsDao.class)
public class ServiceOfferingDetailsDaoImpl extends GenericDaoBase<ServiceOfferingDetailsVO, Long>
implements ServiceOfferingDetailsDao {
protected final SearchBuilder<ServiceOfferingDetailsVO> ServiceOfferingSearch;
protected final SearchBuilder<ServiceOfferingDetailsVO> DetailSearch;
public ServiceOfferingDetailsDaoImpl() {
ServiceOfferingSearch = createSearchBuilder();
ServiceOfferingSearch.and("serviceOfferingId", ServiceOfferingSearch.entity().getServiceOfferingId(), SearchCriteria.Op.EQ);
ServiceOfferingSearch.done();
DetailSearch = createSearchBuilder();
DetailSearch.and("serviceOfferingId", DetailSearch.entity().getServiceOfferingId(), SearchCriteria.Op.EQ);
DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ);
DetailSearch.done();
}
@Override
public ServiceOfferingDetailsVO findDetail(long serviceOfferingId, String name) {
SearchCriteria<ServiceOfferingDetailsVO> sc = DetailSearch.create();
sc.setParameters("serviceOfferingId", serviceOfferingId);
sc.setParameters("name", name);
ServiceOfferingDetailsVO detail = findOneIncludingRemovedBy(sc);
return detail;
}
@Override
public Map<String, String> findDetails(long serviceOfferingId) {
SearchCriteria<ServiceOfferingDetailsVO> sc = ServiceOfferingSearch.create();
sc.setParameters("serviceOfferingId", serviceOfferingId);
List<ServiceOfferingDetailsVO> results = search(sc, null);
Map<String, String> details = new HashMap<String, String>(results.size());
for (ServiceOfferingDetailsVO result : results) {
details.put(result.getName(), result.getValue());
}
return details;
}
@Override
public void deleteDetails(long serviceOfferingId) {
SearchCriteria sc = ServiceOfferingSearch.create();
sc.setParameters("serviceOfferingId", serviceOfferingId);
List<ServiceOfferingDetailsVO> results = search(sc, null);
for (ServiceOfferingDetailsVO result : results) {
remove(result.getId());
}
}
@Override
public void persist(long serviceOfferingId, Map<String, String> details) {
Transaction txn = Transaction.currentTxn();
txn.start();
SearchCriteria<ServiceOfferingDetailsVO> sc = ServiceOfferingSearch.create();
sc.setParameters("serviceOfferingId", serviceOfferingId);
expunge(sc);
for (Map.Entry<String, String> detail : details.entrySet()) {
String value = detail.getValue();
ServiceOfferingDetailsVO vo = new ServiceOfferingDetailsVO(serviceOfferingId, detail.getKey(), value);
persist(vo);
}
txn.commit();
}
}

View File

@ -0,0 +1,29 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-planner-implicit-dedication</artifactId>
<name>Apache CloudStack Plugin - Implicit Dedication Planner</name>
<parent>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloudstack-plugins</artifactId>
<version>4.2.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
</project>

View File

@ -0,0 +1,249 @@
// 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 com.cloud.deploy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.configuration.Config;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.host.HostVO;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.user.Account;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
@Local(value=DeploymentPlanner.class)
public class ImplicitDedicationPlanner extends FirstFitPlanner implements DeploymentClusterPlanner {
private static final Logger s_logger = Logger.getLogger(ImplicitDedicationPlanner.class);
@Inject
private ServiceOfferingDao serviceOfferingDao;
@Inject
private ServiceOfferingDetailsDao serviceOfferingDetailsDao;
@Inject
private ResourceManager resourceMgr;
private int capacityReleaseInterval;
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
super.configure(name, params);
capacityReleaseInterval = NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()), 3600);
return true;
}
@Override
public List<Long> orderClusters(VirtualMachineProfile<? extends VirtualMachine> vmProfile,
DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException {
List<Long> clusterList = super.orderClusters(vmProfile, plan, avoid);
Set<Long> hostsToAvoid = avoid.getHostsToAvoid();
Account account = vmProfile.getOwner();
if (clusterList == null || clusterList.isEmpty()) {
return clusterList;
}
// Check if strict or preferred mode should be used.
boolean preferred = isServiceOfferingUsingPlannerInPreferredMode(vmProfile.getServiceOfferingId());
// Get the list of all the hosts in the given clusters
List<Long> allHosts = new ArrayList<Long>();
for (Long cluster : clusterList) {
List<HostVO> hostsInCluster = resourceMgr.listAllHostsInCluster(cluster);
for (HostVO hostVO : hostsInCluster) {
allHosts.add(hostVO.getId());
}
}
// Go over all the hosts in the cluster and get a list of
// 1. All empty hosts, not running any vms.
// 2. Hosts running vms for this account and created by a service offering which uses an
// implicit dedication planner.
// 3. Hosts running vms created by implicit planner and in strict mode of other accounts.
// 4. Hosts running vms from other account or from this account but created by a service offering which uses
// any planner besides implicit.
Set<Long> emptyHosts = new HashSet<Long>();
Set<Long> hostRunningVmsOfAccount = new HashSet<Long>();
Set<Long> hostRunningStrictImplicitVmsOfOtherAccounts = new HashSet<Long>();
Set<Long> allOtherHosts = new HashSet<Long>();
for (Long host : allHosts) {
List<UserVmVO> userVms = getVmsOnHost(host);
if (userVms == null || userVms.isEmpty()) {
emptyHosts.add(host);
} else if (checkHostSuitabilityForImplicitDedication(account.getAccountId(), userVms)) {
hostRunningVmsOfAccount.add(host);
} else if (checkIfAllVmsCreatedInStrictMode(account.getAccountId(), userVms)) {
hostRunningStrictImplicitVmsOfOtherAccounts.add(host);
} else {
allOtherHosts.add(host);
}
}
// Hosts running vms of other accounts created by ab implicit planner in strict mode should always be avoided.
avoid.addHostList(hostRunningStrictImplicitVmsOfOtherAccounts);
if (!hostRunningVmsOfAccount.isEmpty() && (hostsToAvoid == null ||
!hostsToAvoid.containsAll(hostRunningVmsOfAccount))) {
// Check if any of hosts that are running implicit dedicated vms are available (not in avoid list).
// If so, we'll try and use these hosts.
avoid.addHostList(emptyHosts);
avoid.addHostList(allOtherHosts);
clusterList = getUpdatedClusterList(clusterList, avoid.getHostsToAvoid());
} else if (!emptyHosts.isEmpty() && (hostsToAvoid == null || !hostsToAvoid.containsAll(emptyHosts))) {
// If there aren't implicit resources try on empty hosts
avoid.addHostList(allOtherHosts);
clusterList = getUpdatedClusterList(clusterList, avoid.getHostsToAvoid());
} else if (!preferred) {
// If in strict mode, there is nothing else to try.
clusterList = null;
} else {
// If in preferred mode, check if hosts are available to try, otherwise return an empty cluster list.
if (!allOtherHosts.isEmpty() && (hostsToAvoid == null || !hostsToAvoid.containsAll(allOtherHosts))) {
clusterList = getUpdatedClusterList(clusterList, avoid.getHostsToAvoid());
} else {
clusterList = null;
}
}
return clusterList;
}
private List<UserVmVO> getVmsOnHost(long hostId) {
List<UserVmVO> vms = _vmDao.listUpByHostId(hostId);
List<UserVmVO> vmsByLastHostId = _vmDao.listByLastHostId(hostId);
if (vmsByLastHostId.size() > 0) {
// check if any VMs are within skip.counting.hours, if yes we have to consider the host.
for (UserVmVO stoppedVM : vmsByLastHostId) {
long secondsSinceLastUpdate = (DateUtil.currentGMTTime().getTime() - stoppedVM.getUpdateTime()
.getTime()) / 1000;
if (secondsSinceLastUpdate < capacityReleaseInterval) {
vms.add(stoppedVM);
}
}
}
return vms;
}
private boolean checkHostSuitabilityForImplicitDedication(Long accountId, List<UserVmVO> allVmsOnHost) {
boolean suitable = true;
for (UserVmVO vm : allVmsOnHost) {
if (vm.getAccountId() != accountId) {
s_logger.info("Host " + vm.getHostId() + " found to be unsuitable for implicit dedication as it is " +
"running instances of another account");
suitable = false;
break;
} else {
if (!isImplicitPlannerUsedByOffering(vm.getServiceOfferingId())) {
s_logger.info("Host " + vm.getHostId() + " found to be unsuitable for implicit dedication as it " +
"is running instances of this account which haven't been created using implicit dedication.");
suitable = false;
break;
}
}
}
return suitable;
}
private boolean checkIfAllVmsCreatedInStrictMode(Long accountId, List<UserVmVO> allVmsOnHost) {
boolean createdByImplicitStrict = true;
for (UserVmVO vm : allVmsOnHost) {
if (!isImplicitPlannerUsedByOffering(vm.getServiceOfferingId())) {
s_logger.info("Host " + vm.getHostId() + " found to be running a vm created by a planner other" +
" than implicit.");
createdByImplicitStrict = false;
break;
} else if (isServiceOfferingUsingPlannerInPreferredMode(vm.getServiceOfferingId())) {
s_logger.info("Host " + vm.getHostId() + " found to be running a vm created by an implicit planner" +
" in preferred mode.");
createdByImplicitStrict = false;
break;
}
}
return createdByImplicitStrict;
}
private boolean isImplicitPlannerUsedByOffering(long offeringId) {
boolean implicitPlannerUsed = false;
ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(offeringId);
if (offering == null) {
s_logger.error("Couldn't retrieve the offering by the given id : " + offeringId);
} else {
String plannerName = offering.getDeploymentPlanner();
if (plannerName == null) {
plannerName = _globalDeploymentPlanner;
}
if (plannerName != null && this.getName().equals(plannerName)) {
implicitPlannerUsed = true;
}
}
return implicitPlannerUsed;
}
private boolean isServiceOfferingUsingPlannerInPreferredMode(long serviceOfferingId) {
boolean preferred = false;
Map<String, String> details = serviceOfferingDetailsDao.findDetails(serviceOfferingId);
if (details != null && !details.isEmpty()) {
String preferredAttribute = details.get("ImplicitDedicationMode");
if (preferredAttribute != null && preferredAttribute.equals("Preferred")) {
preferred = true;
}
}
return preferred;
}
private List<Long> getUpdatedClusterList(List<Long> clusterList, Set<Long> hostsSet) {
List<Long> updatedClusterList = new ArrayList<Long>();
for (Long cluster : clusterList) {
List<HostVO> hosts = resourceMgr.listAllHostsInCluster(cluster);
Set<Long> hostsInClusterSet = new HashSet<Long>();
for (HostVO host : hosts) {
hostsInClusterSet.add(host.getId());
}
if (!hostsSet.containsAll(hostsInClusterSet)) {
updatedClusterList.add(cluster);
}
}
return updatedClusterList;
}
@Override
public PlannerResourceUsage getResourceUsage() {
return PlannerResourceUsage.Dedicated;
}
}

View File

@ -0,0 +1,586 @@
// 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.implicitplanner;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.test.utils.SpringUtils;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.cloud.capacity.CapacityManager;
import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.ImplicitDedicationPlanner;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.StorageManager;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.UserContext;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachineProfileImpl;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class ImplicitPlannerTest {
@Inject
ImplicitDedicationPlanner planner = new ImplicitDedicationPlanner();
@Inject
HostDao hostDao;
@Inject
DataCenterDao dcDao;
@Inject
HostPodDao podDao;
@Inject
ClusterDao clusterDao;
@Inject
GuestOSDao guestOSDao;
@Inject
GuestOSCategoryDao guestOSCategoryDao;
@Inject
DiskOfferingDao diskOfferingDao;
@Inject
StoragePoolHostDao poolHostDao;
@Inject
UserVmDao vmDao;
@Inject
VMInstanceDao vmInstanceDao;
@Inject
VolumeDao volsDao;
@Inject
CapacityManager capacityMgr;
@Inject
ConfigurationDao configDao;
@Inject
PrimaryDataStoreDao storagePoolDao;
@Inject
CapacityDao capacityDao;
@Inject
AccountManager accountMgr;
@Inject
StorageManager storageMgr;
@Inject
DataStoreManager dataStoreMgr;
@Inject
ClusterDetailsDao clusterDetailsDao;
@Inject
ServiceOfferingDao serviceOfferingDao;
@Inject
ServiceOfferingDetailsDao serviceOfferingDetailsDao;
@Inject
ResourceManager resourceMgr;
private static long domainId = 5L;
long dataCenterId = 1L;
long accountId = 200L;
long offeringId = 12L;
int noOfCpusInOffering = 1;
int cpuSpeedInOffering = 500;
int ramInOffering = 512;
AccountVO acct = new AccountVO(accountId);
@BeforeClass
public static void setUp() throws ConfigurationException {
}
@Before
public void testSetUp() {
ComponentContext.initComponentsLifeCycle();
acct.setType(Account.ACCOUNT_TYPE_NORMAL);
acct.setAccountName("user1");
acct.setDomainId(domainId);
acct.setId(accountId);
UserContext.registerContext(1, acct, null, true);
}
@Test
public void checkWhenDcInAvoidList() throws InsufficientServerCapacityException {
DataCenterVO mockDc = mock(DataCenterVO.class);
ExcludeList avoids = mock(ExcludeList.class);
@SuppressWarnings("unchecked")
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = mock(VirtualMachineProfileImpl.class);
VMInstanceVO vm = mock(VMInstanceVO.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
when(avoids.shouldAvoid(mockDc)).thenReturn(true);
when(vmProfile.getVirtualMachine()).thenReturn(vm);
when(vm.getDataCenterId()).thenReturn(1L);
when(dcDao.findById(1L)).thenReturn(mockDc);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
assertTrue("Cluster list should be null/empty if the dc is in avoid list",
(clusterList == null || clusterList.isEmpty()));
}
@Test
public void checkStrictModeWithCurrentAccountVmsPresent() throws InsufficientServerCapacityException {
@SuppressWarnings("unchecked")
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = new ExcludeList();
initializeForTest(vmProfile, plan);
initializeForImplicitPlannerTest(false);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
// Validations.
// Check cluster 2 and 3 are not in the cluster list.
// Host 6 and 7 should also be in avoid list.
assertFalse("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty()));
boolean foundNeededCluster = false;
for (Long cluster : clusterList) {
if (cluster != 1) {
fail("Found a cluster that shouldn't have been present, cluster id : " + cluster);
}else {
foundNeededCluster = true;
}
}
assertTrue("Didn't find cluster 1 in the list. It should have been present", foundNeededCluster);
Set<Long> hostsInAvoidList = avoids.getHostsToAvoid();
assertFalse("Host 5 shouldn't have be in the avoid list, but it is present", hostsInAvoidList.contains(5L));
Set<Long> hostsThatShouldBeInAvoidList = new HashSet<Long>();
hostsThatShouldBeInAvoidList.add(6L);
hostsThatShouldBeInAvoidList.add(7L);
assertTrue("Hosts 6 and 7 that should have been present were not found in avoid list" ,
hostsInAvoidList.containsAll(hostsThatShouldBeInAvoidList));
}
@Test
public void checkStrictModeHostWithCurrentAccountVmsFull() throws InsufficientServerCapacityException {
@SuppressWarnings("unchecked")
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = new ExcludeList();
initializeForTest(vmProfile, plan);
initializeForImplicitPlannerTest(false);
// Mark the host 5 with current account vms to be in avoid list.
avoids.addHost(5L);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
// Validations.
// Check cluster 1 and 3 are not in the cluster list.
// Host 5 and 7 should also be in avoid list.
assertFalse("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty()));
boolean foundNeededCluster = false;
for (Long cluster : clusterList) {
if (cluster != 2) {
fail("Found a cluster that shouldn't have been present, cluster id : " + cluster);
}else {
foundNeededCluster = true;
}
}
assertTrue("Didn't find cluster 2 in the list. It should have been present", foundNeededCluster);
Set<Long> hostsInAvoidList = avoids.getHostsToAvoid();
assertFalse("Host 6 shouldn't have be in the avoid list, but it is present", hostsInAvoidList.contains(6L));
Set<Long> hostsThatShouldBeInAvoidList = new HashSet<Long>();
hostsThatShouldBeInAvoidList.add(5L);
hostsThatShouldBeInAvoidList.add(7L);
assertTrue("Hosts 5 and 7 that should have been present were not found in avoid list" ,
hostsInAvoidList.containsAll(hostsThatShouldBeInAvoidList));
}
@Test
public void checkStrictModeNoHostsAvailable() throws InsufficientServerCapacityException {
@SuppressWarnings("unchecked")
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = new ExcludeList();
initializeForTest(vmProfile, plan);
initializeForImplicitPlannerTest(false);
// Mark the host 5 and 6 to be in avoid list.
avoids.addHost(5L);
avoids.addHost(6L);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
// Validations.
// Check cluster list is empty.
assertTrue("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty()));
}
@Test
public void checkPreferredModePreferredHostAvailable() throws InsufficientServerCapacityException {
@SuppressWarnings("unchecked")
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = new ExcludeList();
initializeForTest(vmProfile, plan);
initializeForImplicitPlannerTest(true);
// Mark the host 5 and 6 to be in avoid list.
avoids.addHost(5L);
avoids.addHost(6L);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
// Validations.
// Check cluster 1 and 2 are not in the cluster list.
// Host 5 and 6 should also be in avoid list.
assertFalse("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty()));
boolean foundNeededCluster = false;
for (Long cluster : clusterList) {
if (cluster != 3) {
fail("Found a cluster that shouldn't have been present, cluster id : " + cluster);
} else {
foundNeededCluster = true;
}
}
assertTrue("Didn't find cluster 3 in the list. It should have been present", foundNeededCluster);
Set<Long> hostsInAvoidList = avoids.getHostsToAvoid();
assertFalse("Host 7 shouldn't have be in the avoid list, but it is present", hostsInAvoidList.contains(7L));
Set<Long> hostsThatShouldBeInAvoidList = new HashSet<Long>();
hostsThatShouldBeInAvoidList.add(5L);
hostsThatShouldBeInAvoidList.add(6L);
assertTrue("Hosts 5 and 6 that should have been present were not found in avoid list" ,
hostsInAvoidList.containsAll(hostsThatShouldBeInAvoidList));
}
@Test
public void checkPreferredModeNoHostsAvailable() throws InsufficientServerCapacityException {
@SuppressWarnings("unchecked")
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = new ExcludeList();
initializeForTest(vmProfile, plan);
initializeForImplicitPlannerTest(false);
// Mark the host 5, 6 and 7 to be in avoid list.
avoids.addHost(5L);
avoids.addHost(6L);
avoids.addHost(7L);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
// Validations.
// Check cluster list is empty.
assertTrue("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty()));
}
private void initializeForTest(VirtualMachineProfileImpl<VMInstanceVO> vmProfile, DataCenterDeployment plan) {
DataCenterVO mockDc = mock(DataCenterVO.class);
VMInstanceVO vm = mock(VMInstanceVO.class);
UserVmVO userVm = mock(UserVmVO.class);
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
AccountVO account = mock(AccountVO.class);
when(account.getId()).thenReturn(accountId);
when(account.getAccountId()).thenReturn(accountId);
when(vmProfile.getOwner()).thenReturn(account);
when(vmProfile.getVirtualMachine()).thenReturn(vm);
when(vmProfile.getId()).thenReturn(12L);
when(vmDao.findById(12L)).thenReturn(userVm);
when(userVm.getAccountId()).thenReturn(accountId);
when(vm.getDataCenterId()).thenReturn(dataCenterId);
when(dcDao.findById(1L)).thenReturn(mockDc);
when(plan.getDataCenterId()).thenReturn(dataCenterId);
when(plan.getClusterId()).thenReturn(null);
when(plan.getPodId()).thenReturn(null);
when(configDao.getValue(anyString())).thenReturn("false").thenReturn("CPU");
// Mock offering details.
when(vmProfile.getServiceOffering()).thenReturn(offering);
when(offering.getId()).thenReturn(offeringId);
when(vmProfile.getServiceOfferingId()).thenReturn(offeringId);
when(offering.getCpu()).thenReturn(noOfCpusInOffering);
when(offering.getSpeed()).thenReturn(cpuSpeedInOffering);
when(offering.getRamSize()).thenReturn(ramInOffering);
List<Long> clustersWithEnoughCapacity = new ArrayList<Long>();
clustersWithEnoughCapacity.add(1L);
clustersWithEnoughCapacity.add(2L);
clustersWithEnoughCapacity.add(3L);
when(capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, noOfCpusInOffering * cpuSpeedInOffering,
ramInOffering * 1024L * 1024L, CapacityVO.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
clusterCapacityMap.put(1L, 2048D);
clusterCapacityMap.put(2L, 2048D);
clusterCapacityMap.put(3L, 2048D);
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity =
new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, CapacityVO.CAPACITY_TYPE_CPU,
true)).thenReturn(clustersOrderedByCapacity);
List<Long> disabledClusters = new ArrayList<Long>();
List<Long> clustersWithDisabledPods = new ArrayList<Long>();
when(clusterDao.listDisabledClusters(dataCenterId, null)).thenReturn(disabledClusters);
when(clusterDao.listClustersWithDisabledPods(dataCenterId)).thenReturn(clustersWithDisabledPods);
}
private void initializeForImplicitPlannerTest(boolean preferred) {
String plannerMode = new String("Strict");
if (preferred) {
plannerMode = new String("Preferred");
}
Map<String, String> details = new HashMap<String, String>();
details.put("ImplicitDedicationMode", plannerMode);
when(serviceOfferingDetailsDao.findDetails(offeringId)).thenReturn(details);
// Initialize hosts in clusters
HostVO host1 = mock(HostVO.class);
when(host1.getId()).thenReturn(5L);
HostVO host2 = mock(HostVO.class);
when(host2.getId()).thenReturn(6L);
HostVO host3 = mock(HostVO.class);
when(host3.getId()).thenReturn(7L);
List<HostVO> hostsInCluster1 = new ArrayList<HostVO>();
List<HostVO> hostsInCluster2 = new ArrayList<HostVO>();
List<HostVO> hostsInCluster3 = new ArrayList<HostVO>();
hostsInCluster1.add(host1);
hostsInCluster2.add(host2);
hostsInCluster3.add(host3);
when(resourceMgr.listAllHostsInCluster(1)).thenReturn(hostsInCluster1);
when(resourceMgr.listAllHostsInCluster(2)).thenReturn(hostsInCluster2);
when(resourceMgr.listAllHostsInCluster(3)).thenReturn(hostsInCluster3);
// Mock vms on each host.
long offeringIdForVmsOfThisAccount = 15L;
long offeringIdForVmsOfOtherAccount = 16L;
UserVmVO vm1 = mock(UserVmVO.class);
when(vm1.getAccountId()).thenReturn(accountId);
when(vm1.getServiceOfferingId()).thenReturn(offeringIdForVmsOfThisAccount);
UserVmVO vm2 = mock(UserVmVO.class);
when(vm2.getAccountId()).thenReturn(accountId);
when(vm2.getServiceOfferingId()).thenReturn(offeringIdForVmsOfThisAccount);
// Vm from different account
UserVmVO vm3 = mock(UserVmVO.class);
when(vm3.getAccountId()).thenReturn(201L);
when(vm3.getServiceOfferingId()).thenReturn(offeringIdForVmsOfOtherAccount);
List<UserVmVO> userVmsForHost1 = new ArrayList<UserVmVO>();
List<UserVmVO> userVmsForHost2 = new ArrayList<UserVmVO>();
List<UserVmVO> userVmsForHost3 = new ArrayList<UserVmVO>();
List<UserVmVO> stoppedVmsForHost = new ArrayList<UserVmVO>();
// Host 2 is empty.
userVmsForHost1.add(vm1);
userVmsForHost1.add(vm2);
userVmsForHost3.add(vm3);
when(vmDao.listUpByHostId(5L)).thenReturn(userVmsForHost1);
when(vmDao.listUpByHostId(6L)).thenReturn(userVmsForHost2);
when(vmDao.listUpByHostId(7L)).thenReturn(userVmsForHost3);
when(vmDao.listByLastHostId(5L)).thenReturn(stoppedVmsForHost);
when(vmDao.listByLastHostId(6L)).thenReturn(stoppedVmsForHost);
when(vmDao.listByLastHostId(7L)).thenReturn(stoppedVmsForHost);
// Mock the offering with which the vm was created.
ServiceOfferingVO offeringForVmOfThisAccount = mock(ServiceOfferingVO.class);
when(serviceOfferingDao.findByIdIncludingRemoved(offeringIdForVmsOfThisAccount)).thenReturn(offeringForVmOfThisAccount);
when(offeringForVmOfThisAccount.getDeploymentPlanner()).thenReturn(planner.getName());
ServiceOfferingVO offeringForVMOfOtherAccount = mock(ServiceOfferingVO.class);
when(serviceOfferingDao.findByIdIncludingRemoved(offeringIdForVmsOfOtherAccount)).thenReturn(offeringForVMOfOtherAccount);
when(offeringForVMOfOtherAccount.getDeploymentPlanner()).thenReturn("FirstFitPlanner");
}
@Configuration
@ComponentScan(basePackageClasses = { ImplicitDedicationPlanner.class },
includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)},
useDefaultFilters = false)
public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration {
@Bean
public HostDao hostDao() {
return Mockito.mock(HostDao.class);
}
@Bean
public DataCenterDao dcDao() {
return Mockito.mock(DataCenterDao.class);
}
@Bean
public HostPodDao hostPodDao() {
return Mockito.mock(HostPodDao.class);
}
@Bean
public ClusterDao clusterDao() {
return Mockito.mock(ClusterDao.class);
}
@Bean
public GuestOSDao guestOsDao() {
return Mockito.mock(GuestOSDao.class);
}
@Bean
public GuestOSCategoryDao guestOsCategoryDao() {
return Mockito.mock(GuestOSCategoryDao.class);
}
@Bean
public DiskOfferingDao diskOfferingDao() {
return Mockito.mock(DiskOfferingDao.class);
}
@Bean
public StoragePoolHostDao storagePoolHostDao() {
return Mockito.mock(StoragePoolHostDao.class);
}
@Bean
public UserVmDao userVmDao() {
return Mockito.mock(UserVmDao.class);
}
@Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);
}
@Bean
public VolumeDao volumeDao() {
return Mockito.mock(VolumeDao.class);
}
@Bean
public CapacityManager capacityManager() {
return Mockito.mock(CapacityManager.class);
}
@Bean
public ConfigurationDao configurationDao() {
return Mockito.mock(ConfigurationDao.class);
}
@Bean
public PrimaryDataStoreDao primaryDataStoreDao() {
return Mockito.mock(PrimaryDataStoreDao.class);
}
@Bean
public CapacityDao capacityDao() {
return Mockito.mock(CapacityDao.class);
}
@Bean
public AccountManager accountManager() {
return Mockito.mock(AccountManager.class);
}
@Bean
public StorageManager storageManager() {
return Mockito.mock(StorageManager.class);
}
@Bean
public DataStoreManager dataStoreManager() {
return Mockito.mock(DataStoreManager.class);
}
@Bean
public ClusterDetailsDao clusterDetailsDao() {
return Mockito.mock(ClusterDetailsDao.class);
}
@Bean
public ServiceOfferingDao serviceOfferingDao() {
return Mockito.mock(ServiceOfferingDao.class);
}
@Bean
public ServiceOfferingDetailsDao serviceOfferingDetailsDao() {
return Mockito.mock(ServiceOfferingDetailsDao.class);
}
@Bean
public ResourceManager resourceManager() {
return Mockito.mock(ResourceManager.class);
}
public static class Library implements TypeFilter {
@Override
public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {
ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class);
return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
}
}
}
}

View File

@ -38,6 +38,7 @@
<module>affinity-group-processors/host-anti-affinity</module>
<module>deployment-planners/user-concentrated-pod</module>
<module>deployment-planners/user-dispersing</module>
<module>deployment-planners/implicit-dedication</module>
<module>host-allocators/random</module>
<module>hypervisors/ovm</module>
<module>hypervisors/xen</module>

View File

@ -80,10 +80,11 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
* @param id
* @param useVirtualNetwork
* @param deploymentPlanner
* @param details
* @return ID
*/
ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired,
boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner);
boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map<String, String> details);
/**
* Creates a new disk offering

View File

@ -39,7 +39,6 @@ import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import com.cloud.dc.*;
import com.cloud.dc.dao.*;
import com.cloud.user.*;
@ -105,7 +104,6 @@ import com.cloud.dc.dao.DcDetailsDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.dc.dao.PodVlanMapDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
@ -165,6 +163,7 @@ import com.cloud.server.ConfigurationServer;
import com.cloud.server.ManagementService;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.SwiftVO;
import com.cloud.storage.dao.DiskOfferingDao;
@ -277,6 +276,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Inject
ServiceOfferingDao _serviceOfferingDao;
@Inject
ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
@Inject
DiskOfferingDao _diskOfferingDao;
@Inject
NetworkOfferingDao _networkOfferingDao;
@ -2050,19 +2051,26 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber.intValue(), memory.intValue(), cpuSpeed.intValue(), cmd.getDisplayText(),
localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner());
return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(),
cpuNumber.intValue(), memory.intValue(), cpuSpeed.intValue(), cmd.getDisplayText(),
localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(),
cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails());
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_CREATE, eventDescription = "creating service offering")
public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_type, String name, int cpu, int ramSize, int speed, String displayText,
boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner) {
public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_type,
String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired,
boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag,
Integer networkRate, String deploymentPlanner, Map<String, String> details) {
tags = cleanupTags(tags);
ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, false, tags, isSystem, vm_type,
domainId, hostTag, deploymentPlanner);
if ((offering = _serviceOfferingDao.persist(offering)) != null) {
if (details != null) {
_serviceOfferingDetailsDao.persist(offering.getId(), details);
}
UserContext.current().setEventDetails("Service offering id=" + offering.getId());
return offering;
} else {

View File

@ -431,7 +431,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
*/
@Override
public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, boolean offerHA,
boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner) {
boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map<String, String> details) {
// TODO Auto-generated method stub
return null;
}

View File

@ -19,14 +19,8 @@ package org.apache.cloudstack.networkoffering;
import java.io.IOException;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.dao.*;
import com.cloud.server.ConfigurationServer;
import com.cloud.user.*;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDaoImpl;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.test.utils.SpringUtils;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
@ -44,6 +38,18 @@ import com.cloud.api.query.dao.UserAccountJoinDaoImpl;
import com.cloud.capacity.dao.CapacityDaoImpl;
import com.cloud.cluster.agentlb.dao.HostTransferMapDaoImpl;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.dao.AccountVlanMapDaoImpl;
import com.cloud.dc.dao.ClusterDaoImpl;
import com.cloud.dc.dao.DataCenterDaoImpl;
import com.cloud.dc.dao.DataCenterIpAddressDaoImpl;
import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDao;
import com.cloud.dc.dao.DataCenterVnetDaoImpl;
import com.cloud.dc.dao.DcDetailsDaoImpl;
import com.cloud.dc.dao.HostPodDaoImpl;
import com.cloud.dc.dao.PodVlanDaoImpl;
import com.cloud.dc.dao.PodVlanMapDaoImpl;
import com.cloud.dc.dao.VlanDaoImpl;
import com.cloud.domain.dao.DomainDaoImpl;
import com.cloud.event.dao.UsageEventDaoImpl;
import com.cloud.host.dao.HostDaoImpl;
@ -80,10 +86,11 @@ import com.cloud.network.vpc.dao.PrivateIpDaoImpl;
import com.cloud.network.vpn.RemoteAccessVpnService;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
import com.cloud.offerings.dao.NetworkOfferingServiceMapDaoImpl;
import com.cloud.projects.ProjectManager;
import com.cloud.server.ConfigurationServer;
import com.cloud.server.ManagementService;
import com.cloud.service.dao.ServiceOfferingDaoImpl;
import com.cloud.service.dao.ServiceOfferingDetailsDaoImpl;
import com.cloud.storage.dao.DiskOfferingDaoImpl;
import com.cloud.storage.dao.S3DaoImpl;
import com.cloud.storage.dao.SnapshotDaoImpl;
@ -94,6 +101,11 @@ import com.cloud.storage.s3.S3Manager;
import com.cloud.storage.secondary.SecondaryStorageVmManager;
import com.cloud.storage.swift.SwiftManager;
import com.cloud.tags.dao.ResourceTagsDaoImpl;
import com.cloud.user.AccountDetailsDao;
import com.cloud.user.AccountManager;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.UserContext;
import com.cloud.user.UserContextInitializer;
import com.cloud.user.dao.AccountDaoImpl;
import com.cloud.user.dao.UserDaoImpl;
import com.cloud.vm.dao.InstanceGroupDaoImpl;
@ -110,6 +122,7 @@ import com.cloud.vm.dao.VMInstanceDaoImpl;
DomainDaoImpl.class,
SwiftDaoImpl.class,
ServiceOfferingDaoImpl.class,
ServiceOfferingDetailsDaoImpl.class,
VlanDaoImpl.class,
IPAddressDaoImpl.class,
ResourceTagsDaoImpl.class,

View File

@ -393,7 +393,16 @@ CREATE TABLE `cloud`.`vm_snapshots` (
ALTER TABLE `cloud`.`hypervisor_capabilities` ADD COLUMN `vm_snapshot_enabled` tinyint(1) DEFAULT 0 NOT NULL COMMENT 'Whether VM snapshot is supported by hypervisor';
UPDATE `cloud`.`hypervisor_capabilities` SET `vm_snapshot_enabled`=1 WHERE `hypervisor_type` in ('VMware', 'XenServer');
CREATE TABLE `cloud`.`service_offering_details` (
`id` bigint unsigned NOT NULL auto_increment,
`service_offering_id` bigint unsigned NOT NULL COMMENT 'service offering id',
`name` varchar(255) NOT NULL,
`value` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_service_offering_details__service_offering_id` FOREIGN KEY (`service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE,
CONSTRAINT UNIQUE KEY `uk_service_offering_id_name` (`service_offering_id`, `name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP VIEW IF EXISTS `cloud`.`user_vm_view`;
CREATE VIEW `cloud`.`user_vm_view` AS
select

View File

@ -0,0 +1,232 @@
# 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.
""" P1 tests for Storage motion
"""
#Import Local Modules
import marvin
from marvin.cloudstackTestCase import *
from marvin.cloudstackAPI import *
from marvin.remoteSSHClient import remoteSSHClient
from marvin.integration.lib.utils import *
from marvin.integration.lib.base import *
from marvin.integration.lib.common import *
from nose.plugins.attrib import attr
#Import System modules
import time
_multiprocess_shared_ = True
class Services:
"""Test VM Life Cycle Services
"""
def __init__(self):
self.services = {
"disk_offering":{
"displaytext": "Small",
"name": "Small",
"disksize": 1
},
"account": {
"email": "test@test.com",
"firstname": "Test",
"lastname": "User",
"username": "test",
# Random characters are appended in create account to
# ensure unique username generated each time
"password": "password",
},
"small":
# Create a small virtual machine instance with disk offering
{
"displayname": "testserver",
"username": "root", # VM creds for SSH
"password": "password",
"ssh_port": 22,
"hypervisor": 'XenServer',
"privateport": 22,
"publicport": 22,
"protocol": 'TCP',
},
"service_offerings":
{
"implicitplanner":
{
# Small service offering ID to for change VM
# service offering from medium to small
"name": "Implicit Strict",
"displaytext": "Implicit Strict",
"cpunumber": 1,
"cpuspeed": 500,
"memory": 512,
"deploymentplanner": "ImplicitDedicationPlanner"
}
},
"template": {
"displaytext": "Cent OS Template",
"name": "Cent OS Template",
"passwordenabled": True,
},
"diskdevice": '/dev/xvdd',
# Disk device where ISO is attached to instance
"mount_dir": "/mnt/tmp",
"sleep": 60,
"timeout": 10,
#Migrate VM to hostid
"ostype": 'CentOS 5.3 (64-bit)',
# CentOS 5.3 (64-bit)
}
class TestImplicitPlanner(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.api_client = super(TestImplicitPlanner, cls).getClsTestClient().getApiClient()
cls.services = Services().services
# Get Zone, Domain and templates
domain = get_domain(cls.api_client, cls.services)
cls.zone = get_zone(cls.api_client, cls.services)
cls.services['mode'] = cls.zone.networktype
template = get_template(
cls.api_client,
cls.zone.id,
cls.services["ostype"]
)
# Set Zones and disk offerings
cls.services["small"]["zoneid"] = cls.zone.id
cls.services["small"]["template"] = template.id
# Create VMs, NAT Rules etc
cls.account = Account.create(
cls.api_client,
cls.services["account"],
domainid=domain.id
)
cls.small_offering = ServiceOffering.create(
cls.api_client,
cls.services["service_offerings"]["implicitplanner"]
)
cls._cleanup = [
cls.small_offering,
cls.account
]
@classmethod
def tearDownClass(cls):
cls.api_client = super(TestImplicitPlanner, cls).getClsTestClient().getApiClient()
cleanup_resources(cls.api_client, cls._cleanup)
return
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
def tearDown(self):
#Clean up, terminate the created ISOs
cleanup_resources(self.apiclient, self.cleanup)
return
# This test requires multi host and at least one host which is empty (no vms should
# be running on that host). It uses an implicit planner to deploy instances and the
# instances of a new account should go to an host that doesn't have vms of any other
# account.
@attr(tags = ["advanced", "basic", "multihosts", "implicitplanner"])
def test_01_deploy_vm_with_implicit_planner(self):
"""Test implicit planner is placing vms of an account on implicitly dedicated hosts.
"""
# Validate the following
# 1. Deploy a vm using implicit planner. It should go on to a
# host that is empty (not running vms of any other account)
# 2. Deploy another vm it should get deployed on the same host.
#create a virtual machine
virtual_machine_1 = VirtualMachine.create(
self.api_client,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.small_offering.id,
mode=self.services["mode"]
)
list_vm_response_1 = list_virtual_machines(
self.apiclient,
id=virtual_machine_1.id
)
self.assertEqual(
isinstance(list_vm_response_1, list),
True,
"Check list response returns a valid list"
)
self.assertNotEqual(
list_vm_response_1,
None,
"Check virtual machine is listVirtualMachines"
)
vm_response_1 = list_vm_response_1[0]
self.assertEqual(
vm_response_1.id,
virtual_machine_1.id,
"Check virtual machine ID of VM"
)
virtual_machine_2 = VirtualMachine.create(
self.api_client,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.small_offering.id,
mode=self.services["mode"]
)
list_vm_response_2 = list_virtual_machines(
self.apiclient,
id=virtual_machine_2.id
)
self.assertEqual(
isinstance(list_vm_response_2, list),
True,
"Check list response returns a valid list"
)
self.assertNotEqual(
list_vm_response_2,
None,
"Check virtual machine is listVirtualMachines"
)
vm_response_2 = list_vm_response_2[0]
self.assertEqual(
vm_response_2.id,
virtual_machine_2.id,
"Check virtual machine ID of VM"
)
self.assertEqual(
vm_response_1.hostid,
vm_response_2.hostid,
"Check both vms have the same host id"
)
return

View File

@ -1268,6 +1268,10 @@ class ServiceOffering:
if "tags" in services:
cmd.tags = services["tags"]
if "deploymentplanner" in services:
cmd.deploymentplanner = services["deploymentplanner"]
# Service Offering private to that domain
if domainid:
cmd.domainid = domainid