Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 62482DFC3 for ; Fri, 17 May 2013 06:25:35 +0000 (UTC) Received: (qmail 61662 invoked by uid 500); 17 May 2013 06:25:34 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 61484 invoked by uid 500); 17 May 2013 06:25:34 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 61450 invoked by uid 99); 17 May 2013 06:25:33 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 17 May 2013 06:25:33 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 2CB3622BDA; Fri, 17 May 2013 06:25:33 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: devdeep@apache.org To: commits@cloudstack.apache.org Message-Id: <12feb2aea454469a8147bc46b62b4f4c@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: git commit: updated refs/heads/master to adbebc1 Date: Fri, 17 May 2013 06:25:33 +0000 (UTC) Updated Branches: refs/heads/master caf0dd22b -> adbebc189 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. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/adbebc18 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/adbebc18 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/adbebc18 Branch: refs/heads/master Commit: adbebc1892cf88df8c801291a88bc836c99b8c14 Parents: caf0dd2 Author: Devdeep Singh Authored: Fri May 17 11:14:25 2013 +0530 Committer: Devdeep Singh Committed: Fri May 17 11:40:31 2013 +0530 ---------------------------------------------------------------------- api/src/com/cloud/deploy/DeploymentPlanner.java | 7 + .../org/apache/cloudstack/api/ApiConstants.java | 1 + .../admin/offering/CreateServiceOfferingCmd.java | 16 + client/pom.xml | 5 + client/tomcatconf/applicationContext.xml.in | 6 +- client/tomcatconf/componentContext.xml.in | 1 + .../cloud/service/ServiceOfferingDetailsVO.java | 73 ++ .../src/com/cloud/service/ServiceOfferingVO.java | 27 + .../com/cloud/service/dao/ServiceOfferingDao.java | 2 + .../cloud/service/dao/ServiceOfferingDaoImpl.java | 19 +- .../service/dao/ServiceOfferingDetailsDao.java | 29 + .../service/dao/ServiceOfferingDetailsDaoImpl.java | 98 +++ .../implicit-dedication/pom.xml | 29 + .../cloud/deploy/ImplicitDedicationPlanner.java | 249 ++++++ .../implicitplanner/ImplicitPlannerTest.java | 586 +++++++++++++++ plugins/pom.xml | 1 + .../cloud/configuration/ConfigurationManager.java | 3 +- .../configuration/ConfigurationManagerImpl.java | 20 +- .../cloud/vpc/MockConfigurationManagerImpl.java | 2 +- .../networkoffering/ChildTestConfiguration.java | 27 +- setup/db/db/schema-410to420.sql | 11 +- .../integration/component/test_implicit_planner.py | 232 ++++++ tools/marvin/marvin/integration/lib/base.py | 4 + 23 files changed, 1430 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/api/src/com/cloud/deploy/DeploymentPlanner.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/deploy/DeploymentPlanner.java b/api/src/com/cloud/deploy/DeploymentPlanner.java index eb56a59..769da39 100644 --- a/api/src/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/com/cloud/deploy/DeploymentPlanner.java @@ -213,6 +213,13 @@ public interface DeploymentPlanner extends Adapter { _hostIds.add(hostId); } + public void addHostList(Collection hostList) { + if (_hostIds == null) { + _hostIds = new HashSet(); + } + _hostIds.addAll(hostList); + } + public boolean shouldAvoid(Host host) { if (_dcIds != null && _dcIds.contains(host.getDataCenterId())) { return true; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/api/src/org/apache/cloudstack/api/ApiConstants.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 8d7739c..cf093bf 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -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"; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index c155b70..4c54a4e 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -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 details; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -155,6 +161,16 @@ public class CreateServiceOfferingCmd extends BaseCmd { return deploymentPlanner; } + public Map getDetails() { + if (details == null || details.isEmpty()) { + return null; + } + + Collection paramsCollection = details.values(); + Map params = (Map)(paramsCollection.toArray())[0]; + return params; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/client/pom.xml ---------------------------------------------------------------------- diff --git a/client/pom.xml b/client/pom.xml index 197ba27..0c38ecb 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -133,6 +133,11 @@ org.apache.cloudstack + cloud-plugin-planner-implicit-dedication + ${project.version} + + + org.apache.cloudstack cloud-plugin-host-allocator-random ${project.version} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/client/tomcatconf/applicationContext.xml.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 1d1eca4..b500fde 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -370,7 +370,7 @@ - + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java new file mode 100644 index 0000000..b005c73 --- /dev/null +++ b/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java @@ -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; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/engine/schema/src/com/cloud/service/ServiceOfferingVO.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java index fd31d30..9a262c5 100755 --- a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java @@ -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 details; + protected ServiceOfferingVO() { super(); } @@ -225,4 +233,23 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering return deploymentPlanner; } + public Map 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 details) { + this.details = details; + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java index 589de7c..7da7208 100644 --- a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java @@ -31,4 +31,6 @@ public interface ServiceOfferingDao extends GenericDao List findServiceOfferingByDomainId(Long domainId); List findSystemOffering(Long domainId, Boolean isSystem, String vm_type); ServiceOfferingVO persistDeafultServiceOffering(ServiceOfferingVO offering); + void loadDetails(ServiceOfferingVO serviceOffering); + void saveDetails(ServiceOfferingVO serviceOffering); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java index 062103e..14b2abf 100644 --- a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -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 implements ServiceOfferingDao { protected static final Logger s_logger = Logger.getLogger(ServiceOfferingDaoImpl.class); + @Inject protected ServiceOfferingDetailsDao detailsDao; + protected final SearchBuilder UniqueNameSearch; protected final SearchBuilder ServiceOfferingsByDomainIdSearch; protected final SearchBuilder SystemServiceOffering; @@ -154,4 +157,18 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase details = detailsDao.findDetails(serviceOffering.getId()); + serviceOffering.setDetails(details); + } + + @Override + public void saveDetails(ServiceOfferingVO serviceOffering) { + Map details = serviceOffering.getDetails(); + if (details != null) { + detailsDao.persist(serviceOffering.getId(), details); + } + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDao.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDao.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDao.java new file mode 100644 index 0000000..3816910 --- /dev/null +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDao.java @@ -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 { + Map findDetails(long serviceOfferingId); + void persist(long serviceOfferingId, Map details); + ServiceOfferingDetailsVO findDetail(long serviceOfferingId, String name); + void deleteDetails(long serviceOfferingId); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java new file mode 100644 index 0000000..91d736a --- /dev/null +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java @@ -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 + implements ServiceOfferingDetailsDao { + protected final SearchBuilder ServiceOfferingSearch; + protected final SearchBuilder 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 sc = DetailSearch.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + sc.setParameters("name", name); + ServiceOfferingDetailsVO detail = findOneIncludingRemovedBy(sc); + return detail; + } + + @Override + public Map findDetails(long serviceOfferingId) { + SearchCriteria sc = ServiceOfferingSearch.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + List results = search(sc, null); + Map details = new HashMap(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 results = search(sc, null); + for (ServiceOfferingDetailsVO result : results) { + remove(result.getId()); + } + } + + @Override + public void persist(long serviceOfferingId, Map details) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + SearchCriteria sc = ServiceOfferingSearch.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + expunge(sc); + + for (Map.Entry detail : details.entrySet()) { + String value = detail.getValue(); + ServiceOfferingDetailsVO vo = new ServiceOfferingDetailsVO(serviceOfferingId, detail.getKey(), value); + persist(vo); + } + txn.commit(); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/plugins/deployment-planners/implicit-dedication/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/deployment-planners/implicit-dedication/pom.xml b/plugins/deployment-planners/implicit-dedication/pom.xml new file mode 100644 index 0000000..1855592 --- /dev/null +++ b/plugins/deployment-planners/implicit-dedication/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + cloud-plugin-planner-implicit-dedication + Apache CloudStack Plugin - Implicit Dedication Planner + + org.apache.cloudstack + cloudstack-plugins + 4.2.0-SNAPSHOT + ../../pom.xml + + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java ---------------------------------------------------------------------- diff --git a/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java b/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java new file mode 100644 index 0000000..d47d8f5 --- /dev/null +++ b/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java @@ -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 params) throws ConfigurationException { + super.configure(name, params); + capacityReleaseInterval = NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()), 3600); + return true; + } + + @Override + public List orderClusters(VirtualMachineProfile vmProfile, + DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException { + List clusterList = super.orderClusters(vmProfile, plan, avoid); + Set 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 allHosts = new ArrayList(); + for (Long cluster : clusterList) { + List 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 emptyHosts = new HashSet(); + Set hostRunningVmsOfAccount = new HashSet(); + Set hostRunningStrictImplicitVmsOfOtherAccounts = new HashSet(); + Set allOtherHosts = new HashSet(); + for (Long host : allHosts) { + List 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 getVmsOnHost(long hostId) { + List vms = _vmDao.listUpByHostId(hostId); + List 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 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 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 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 getUpdatedClusterList(List clusterList, Set hostsSet) { + List updatedClusterList = new ArrayList(); + for (Long cluster : clusterList) { + List hosts = resourceMgr.listAllHostsInCluster(cluster); + Set hostsInClusterSet = new HashSet(); + for (HostVO host : hosts) { + hostsInClusterSet.add(host.getId()); + } + + if (!hostsSet.containsAll(hostsInClusterSet)) { + updatedClusterList.add(cluster); + } + } + + return updatedClusterList; + } + + @Override + public PlannerResourceUsage getResourceUsage() { + return PlannerResourceUsage.Dedicated; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java ---------------------------------------------------------------------- diff --git a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java new file mode 100644 index 0000000..4450760 --- /dev/null +++ b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java @@ -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 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 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 vmProfile = mock(VirtualMachineProfileImpl.class); + DataCenterDeployment plan = mock(DataCenterDeployment.class); + ExcludeList avoids = new ExcludeList(); + + initializeForTest(vmProfile, plan); + + initializeForImplicitPlannerTest(false); + + List 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 hostsInAvoidList = avoids.getHostsToAvoid(); + assertFalse("Host 5 shouldn't have be in the avoid list, but it is present", hostsInAvoidList.contains(5L)); + Set hostsThatShouldBeInAvoidList = new HashSet(); + 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 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 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 hostsInAvoidList = avoids.getHostsToAvoid(); + assertFalse("Host 6 shouldn't have be in the avoid list, but it is present", hostsInAvoidList.contains(6L)); + Set hostsThatShouldBeInAvoidList = new HashSet(); + 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 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 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 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 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 hostsInAvoidList = avoids.getHostsToAvoid(); + assertFalse("Host 7 shouldn't have be in the avoid list, but it is present", hostsInAvoidList.contains(7L)); + Set hostsThatShouldBeInAvoidList = new HashSet(); + 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 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 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 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 clustersWithEnoughCapacity = new ArrayList(); + 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 clusterCapacityMap = new HashMap(); + clusterCapacityMap.put(1L, 2048D); + clusterCapacityMap.put(2L, 2048D); + clusterCapacityMap.put(3L, 2048D); + Pair, Map> clustersOrderedByCapacity = + new Pair, Map>(clustersWithEnoughCapacity, clusterCapacityMap); + when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, CapacityVO.CAPACITY_TYPE_CPU, + true)).thenReturn(clustersOrderedByCapacity); + + List disabledClusters = new ArrayList(); + List clustersWithDisabledPods = new ArrayList(); + 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 details = new HashMap(); + 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 hostsInCluster1 = new ArrayList(); + List hostsInCluster2 = new ArrayList(); + List hostsInCluster3 = new ArrayList(); + 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 userVmsForHost1 = new ArrayList(); + List userVmsForHost2 = new ArrayList(); + List userVmsForHost3 = new ArrayList(); + List stoppedVmsForHost = new ArrayList(); + // 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); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/plugins/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/pom.xml b/plugins/pom.xml index e49fac9..2efa248 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -38,6 +38,7 @@ affinity-group-processors/host-anti-affinity deployment-planners/user-concentrated-pod deployment-planners/user-dispersing + deployment-planners/implicit-dedication host-allocators/random hypervisors/ovm hypervisors/xen http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/server/src/com/cloud/configuration/ConfigurationManager.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java index d0ae914..8db037b 100755 --- a/server/src/com/cloud/configuration/ConfigurationManager.java +++ b/server/src/com/cloud/configuration/ConfigurationManager.java @@ -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 details); /** * Creates a new disk offering http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/server/src/com/cloud/configuration/ConfigurationManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 9e0c847..52d6176 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -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 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 { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index ba18fa1..4fb182a 100755 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -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 details) { // TODO Auto-generated method stub return null; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java ---------------------------------------------------------------------- diff --git a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java index 7ffbe32..a825699 100644 --- a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java +++ b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java @@ -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, http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/setup/db/db/schema-410to420.sql ---------------------------------------------------------------------- diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 442a544..fe66207 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -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 http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/test/integration/component/test_implicit_planner.py ---------------------------------------------------------------------- diff --git a/test/integration/component/test_implicit_planner.py b/test/integration/component/test_implicit_planner.py new file mode 100644 index 0000000..ffcd248 --- /dev/null +++ b/test/integration/component/test_implicit_planner.py @@ -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 \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/adbebc18/tools/marvin/marvin/integration/lib/base.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index ecdc841..a811f14 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -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