Repository: cloudstack Updated Branches: refs/heads/master c10f8a10e -> 1de0cb709
restore barematel work after merge Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/1de0cb70 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/1de0cb70 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/1de0cb70 Branch: refs/heads/master Commit: 1de0cb709483090926cbef48d1d677f618f9c17f Parents: c10f8a1 Author: Daan Hoogland <d...@onecht.net> Authored: Fri Oct 17 10:24:01 2014 +0200 Committer: Daan Hoogland <d...@onecht.net> Committed: Fri Oct 17 10:24:01 2014 +0200 ---------------------------------------------------------------------- api/src/com/cloud/event/EventTypes.java | 1 + .../PrepareKickstartPxeServerCommand.java | 84 ------ .../core/spring-baremetal-core-context.xml | 8 +- .../baremetal/manager/BaremetalManager.java | 3 + .../baremetal/manager/BaremetalManagerImpl.java | 3 + .../baremetal/manager/BaremetalVlanManager.java | 1 + .../manager/BaremetalVlanManagerImpl.java | 7 +- .../networkservice/BareMetalResourceBase.java | 47 ++- .../BaremetalKickStartPxeResource.java | 1 - .../BaremetalKickStartServiceImpl.java | 12 +- .../networkservice/BaremetalPxeElement.java | 5 +- .../Force10BaremetalSwitchBackend.java | 120 ++++++-- .../PrepareKickstartPxeServerCommand.java | 83 ++++++ .../BaremetalProvisionDoneNotificationCmd.java | 87 ++++++ server/src/com/cloud/configuration/Config.java | 36 ++- .../deploy/DeploymentPlanningManagerImpl.java | 11 +- .../VirtualNetworkApplianceManagerImpl.java | 2 + .../com/cloud/resource/ResourceManagerImpl.java | 15 - .../com/cloud/storage/VolumeApiServiceImpl.java | 12 +- .../cloud/storage/VolumeApiServiceImplTest.java | 24 +- .../patches/debian/config/etc/dnsmasq.conf.tmpl | 6 +- .../config/opt/cloud/bin/baremetal_snat.sh | 21 +- .../debian/config/opt/cloud/bin/prepare_pxe.sh | 28 +- .../component/test_deploy_vgpu_vm.py | 291 +------------------ .../com/cloud/utils/xmlobject/XmlObject.java | 15 +- .../cloud/utils/xmlobject/XmlObjectParser.java | 22 +- .../cloud/utils/xmlobject/TestXmlObject.java | 5 + 27 files changed, 462 insertions(+), 488 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/api/src/com/cloud/event/EventTypes.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 7477152..3a1d58a 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -454,6 +454,7 @@ public class EventTypes { public static final String EVENT_BAREMETAL_PXE_SERVER_ADD = "PHYSICAL.PXE.ADD"; public static final String EVENT_BAREMETAL_PXE_SERVER_DELETE = "PHYSICAL.PXE.DELETE"; public static final String EVENT_BAREMETAL_RCT_ADD = "BAREMETAL.RCT.ADD"; + public static final String EVENT_BAREMETAL_PROVISION_DONE = "BAREMETAL.PROVISION.DONE"; public static final String EVENT_AFFINITY_GROUP_CREATE = "AG.CREATE"; public static final String EVENT_AFFINITY_GROUP_DELETE = "AG.DELETE"; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/core/src/com/cloud/agent/api/baremetal/PrepareKickstartPxeServerCommand.java ---------------------------------------------------------------------- diff --git a/core/src/com/cloud/agent/api/baremetal/PrepareKickstartPxeServerCommand.java b/core/src/com/cloud/agent/api/baremetal/PrepareKickstartPxeServerCommand.java deleted file mode 100755 index b8fb5d0..0000000 --- a/core/src/com/cloud/agent/api/baremetal/PrepareKickstartPxeServerCommand.java +++ /dev/null @@ -1,84 +0,0 @@ -// -// 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.agent.api.baremetal; - -import com.cloud.agent.api.Command; - -public class PrepareKickstartPxeServerCommand extends Command { - private String ksFile; - private String templateUuid; - private String mac; - private String ksDevice; - private String kernel; - private String initrd; - - @Override - public boolean executeInSequence() { - return true; - } - - public String getKsFile() { - return ksFile; - } - - public void setKsFile(String ksFile) { - this.ksFile = ksFile; - } - - public String getKernel() { - return kernel; - } - - public void setKernel(String kernel) { - this.kernel = kernel; - } - - public String getInitrd() { - return initrd; - } - - public void setInitrd(String initrd) { - this.initrd = initrd; - } - - public String getTemplateUuid() { - return templateUuid; - } - - public void setTemplateUuid(String templateUuid) { - this.templateUuid = templateUuid; - } - - public String getMac() { - return mac; - } - - public void setMac(String mac) { - this.mac = mac; - } - - public String getKsDevice() { - return ksDevice; - } - - public void setKsDevice(String ksDevice) { - this.ksDevice = ksDevice; - } -} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml b/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml index ae28efa..a153e7f 100755 --- a/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml +++ b/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml @@ -28,7 +28,13 @@ > <bean id="BaremetalManager" class="com.cloud.baremetal.manager.BaremetalManagerImpl" /> - <bean id="BaremetalVlanManager" class="com.cloud.baremetal.manager.BaremetalVlanManagerImpl" /> + <bean id="BaremetalVlanManager" class="com.cloud.baremetal.manager.BaremetalVlanManagerImpl" > + <property name="backends"> + <map> + <entry key="Force10" value-ref="Force10BaremetalSwitchBackend"/> + </map> + </property> + </bean> <bean id="Force10BaremetalSwitchBackend" class="com.cloud.baremetal.networkservice.Force10BaremetalSwitchBackend" /> <bean id="BaremetalKickStartPxeService" http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java index 5db29d2..629153a 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java @@ -20,9 +20,12 @@ package com.cloud.baremetal.manager; import com.cloud.utils.component.Manager; import com.cloud.utils.component.PluggableService; +import org.apache.cloudstack.api.BaremetalProvisionDoneNotificationCmd; public interface BaremetalManager extends Manager, PluggableService { public static final String EchoSecurityGroupAgent = "EchoSecurityGroupAgent"; public static final String ExternalBaremetalSystemUrl = "ExternalBaremetalSystemUrl"; public static final String DO_PXE = "doPxe"; + + void notifyProvisionDone(BaremetalProvisionDoneNotificationCmd cmd); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java index 076a1bc..f826ae9 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java @@ -52,6 +52,8 @@ public class BaremetalManagerImpl extends ManagerBase implements BaremetalManage @Inject protected HostDao _hostDao; + @Inject + protected VMInstanceDao vmDao; @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { @@ -116,6 +118,7 @@ public class BaremetalManagerImpl extends ManagerBase implements BaremetalManage public List<Class<?>> getCommands() { List<Class<?>> cmds = new ArrayList<Class<?>>(); cmds.add(AddBaremetalHostCmd.class); + cmds.add(BaremetalProvisionDoneNotificationCmd.class); return cmds; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManager.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManager.java index 09215ea..b2a1be9 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManager.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManager.java @@ -18,6 +18,7 @@ package com.cloud.baremetal.manager; import com.cloud.baremetal.networkservice.BaremetalRctResponse; +import com.cloud.baremetal.networkservice.BaremetalSwitchBackend; import com.cloud.deploy.DeployDestination; import com.cloud.network.Network; import com.cloud.utils.component.Manager; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java index 3728a31..c8473b6 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java @@ -48,7 +48,6 @@ import javax.inject.Inject; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -70,13 +69,17 @@ public class BaremetalVlanManagerImpl extends ManagerBase implements BaremetalVl @Inject private AccountManager acntMgr; - private Map<String, BaremetalSwitchBackend> backends = new HashMap<>(); + private Map<String, BaremetalSwitchBackend> backends; private class RackPair { BaremetalRct.Rack rack; BaremetalRct.HostEntry host; } + public void setBackends(Map<String, BaremetalSwitchBackend> backends) { + this.backends = backends; + } + @Override public BaremetalRctResponse addRct(AddBaremetalRctCmd cmd) { try { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java index 8ed50ef..ab863dc 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java @@ -59,12 +59,15 @@ import com.cloud.host.Host.Type; import com.cloud.hypervisor.Hypervisor; import com.cloud.resource.ServerResource; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; import com.cloud.utils.script.Script2; import com.cloud.utils.script.Script2.ParamType; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.api.ApiConstants; @@ -107,6 +110,8 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource protected Script2 _bootOrRebootCommand; protected String _vmName; protected int ipmiRetryTimes = 5; + protected boolean provisionDoneNotificationOn = false; + protected int isProvisionDoneNotificationTimeout = 1800; protected ConfigurationDao configDao; protected VMInstanceDao vmDao; @@ -181,6 +186,13 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource s_logger.debug(e.getMessage(), e); } + try { + provisionDoneNotificationOn = Boolean.valueOf(configDao.getValue(Config.BaremetalProvisionDoneNotificationEnabled.key())); + isProvisionDoneNotificationTimeout = Integer.valueOf(configDao.getValue(Config.BaremetalProvisionDoneNotificationTimeout.key())); + } catch (Exception e) { + s_logger.debug(e.getMessage(), e); + } + String injectScript = "scripts/util/ipmi.py"; String scriptPath = Script.findScript("", injectScript); if (scriptPath == null) { @@ -392,7 +404,7 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource VMInstanceVO vm = vms.get(0); SecurityGroupHttpClient client = new SecurityGroupHttpClient(); HashMap<String, Pair<Long, Long>> nwGrpStates = client.sync(vm.getInstanceName(), vm.getId(), vm.getPrivateIpAddress()); - return new PingRoutingWithNwGroupsCommand(getType(), id, getHostVmStateReport(), nwGrpStates); + return new PingRoutingWithNwGroupsCommand(getType(), id, null, nwGrpStates); } } else { return new PingRoutingCommand(getType(), id, null); @@ -581,6 +593,39 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource } } + if (provisionDoneNotificationOn) { + QueryBuilder<VMInstanceVO> q = QueryBuilder.create(VMInstanceVO.class); + q.and(q.entity().getInstanceName(), SearchCriteria.Op.EQ, vm.getName()); + VMInstanceVO vmvo = q.find(); + + if (vmvo.getLastHostId() == null) { + // this is new created vm + long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(isProvisionDoneNotificationTimeout); + while (timeout > System.currentTimeMillis()) { + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + s_logger.warn(e.getMessage(), e); + } + + q = QueryBuilder.create(VMInstanceVO.class); + q.and(q.entity().getInstanceName(), SearchCriteria.Op.EQ, vm.getName()); + vmvo = q.find(); + if (vmvo == null) { + return new StartAnswer(cmd, String.format("cannot find vm[name:%s] while waiting for baremtal provision done notification", vm.getName())); + } + + if (VirtualMachine.State.Running == vmvo.getState()) { + return new StartAnswer(cmd); + } + + s_logger.debug(String.format("still wait for baremetal provision done notification for vm[name:%s], current vm state is %s", vmvo.getInstanceName(), vmvo.getState())); + } + + return new StartAnswer(cmd, String.format("timeout after %s seconds, no baremetal provision done notification received. vm[name:%s] failed to start", isProvisionDoneNotificationTimeout, vm.getName())); + } + } + s_logger.debug("Start bare metal vm " + vm.getName() + "successfully"); _vmName = vm.getName(); return new StartAnswer(cmd); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java index 75be597..260f4f1 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java @@ -34,7 +34,6 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.HostVmStateReportEntry; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; -import com.cloud.agent.api.baremetal.PrepareKickstartPxeServerCommand; import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java index f668155..7f6b0bf 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java @@ -30,16 +30,17 @@ import java.util.Map; import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.configuration.Config; +import org.apache.log4j.Logger; + import org.apache.cloudstack.api.AddBaremetalKickStartPxeCmd; import org.apache.cloudstack.api.AddBaremetalPxeCmd; import org.apache.cloudstack.api.ListBaremetalPxeServersCmd; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; -import com.cloud.agent.api.baremetal.PrepareKickstartPxeServerCommand; import com.cloud.baremetal.database.BaremetalPxeDao; import com.cloud.baremetal.database.BaremetalPxeVO; import com.cloud.baremetal.networkservice.BaremetalPxeManager.BaremetalPxeType; @@ -223,6 +224,11 @@ public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase imple throw new CloudRuntimeException(String.format("cannot find management nic on virtual router[id:%s]", vr.getId())); } + String internalServerIp = _configDao.getValue(Config.BaremetalInternalStorageServer.key()); + if (internalServerIp == null) { + throw new CloudRuntimeException(String.format("please specify 'baremetal.internal.storage.server.ip', which is the http server/nfs server storing kickstart files and ISO files, in global setting")); + } + List<String> tuple = parseKickstartUrl(profile); String cmd = String.format("/opt/cloud/bin/prepare_pxe.sh %s %s %s %s %s %s", tuple.get(1), tuple.get(2), profile.getTemplate().getUuid(), String.format("01-%s", nic.getMacAddress().replaceAll(":", "-")).toLowerCase(), tuple.get(0), nic.getMacAddress().toLowerCase()); @@ -232,7 +238,7 @@ public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase imple throw new CloudRuntimeException(String.format("failed preparing PXE in virtual router[id:%s], because %s", vr.getId(), ret.second())); } - String internalServerIp = "10.223.110.231"; + //String internalServerIp = "10.223.110.231"; cmd = String.format("/opt/cloud/bin/baremetal_snat.sh %s %s %s", mgmtNic.getIp4Address(), internalServerIp, mgmtNic.getGateway()); s_logger.debug(String.format("prepare SNAT on virtual router[ip:%s], cmd: %s", mgmtNic.getIp4Address(), cmd)); ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, cmd); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java index 5d20d31..10b81c2 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java @@ -21,6 +21,7 @@ package com.cloud.baremetal.networkservice; import com.cloud.baremetal.database.BaremetalPxeVO; import com.cloud.baremetal.manager.BaremetalVlanManager; import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeployDestination; @@ -139,11 +140,9 @@ public class BaremetalPxeElement extends AdapterBase implements NetworkElement { } } - /* if (dest.getDataCenter().getNetworkType() == DataCenter.NetworkType.Advanced){ prepareVlan(network, dest); } - */ return true; } @@ -159,12 +158,10 @@ public class BaremetalPxeElement extends AdapterBase implements NetworkElement { return false; } - /* DataCenterVO dc = zoneDao.findById(vm.getVirtualMachine().getDataCenterId()); if (dc.getNetworkType() == DataCenter.NetworkType.Advanced) { releaseVlan(network, vm); } - */ return true; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java old mode 100644 new mode 100755 index 9009b02..c34a6cd --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java @@ -32,11 +32,16 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -46,17 +51,38 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { private Logger logger = Logger.getLogger(Force10BaremetalSwitchBackend.class); public static final String TYPE = "Force10"; + private static List<HttpStatus> successHttpStatusCode = new ArrayList<>(); + { + successHttpStatusCode.add(HttpStatus.OK); + successHttpStatusCode.add(HttpStatus.ACCEPTED); + successHttpStatusCode.add(HttpStatus.CREATED); + successHttpStatusCode.add(HttpStatus.NO_CONTENT); + successHttpStatusCode.add(HttpStatus.PARTIAL_CONTENT); + successHttpStatusCode.add(HttpStatus.RESET_CONTENT); + successHttpStatusCode.add(HttpStatus.ALREADY_REPORTED); + } + RestTemplate rest = new RestTemplate(); - - private String buildLink(String switchIp, Integer vlan) { + { + // fake error handler, we handle error in business logic code + rest.setErrorHandler(new ResponseErrorHandler() { + @Override + public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { + return false; + } + + @Override + public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { + } + }); + } + + private String buildLink(String switchIp, String path) { UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); builder.scheme("http"); builder.host(switchIp); builder.port(8008); - builder.path("/api/running/ftos/interface/vlan"); - if (vlan != null) { - builder.path(vlan.toString()); - } + builder.path(path); return builder.build().toUriString(); } @@ -67,29 +93,36 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { @Override public void prepareVlan(BaremetalVlanStruct struct) { - String link = buildLink(struct.getSwitchIp(), struct.getVlan()); + String link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/vlan/%s", struct.getVlan())); HttpHeaders headers = createBasicAuthenticationHeader(struct); HttpEntity<String> request = new HttpEntity<>(headers); ResponseEntity rsp = rest.exchange(link, HttpMethod.GET, request, String.class); + logger.debug(String.format("http get: %s", link)); if (rsp.getStatusCode() == HttpStatus.NOT_FOUND) { PortInfo port = new PortInfo(struct); - XmlObject xml = new XmlObject("vlan").putElement("vlan-id", String.valueOf(struct.getVlan())).putElement("tagged", - new XmlObject(port.interfaceType).putElement("name", port.port) - ).putElement("shutdown", "false"); - request = new HttpEntity<>(xml.toString(), headers); - link = buildLink(struct.getSwitchIp(), null); - rsp = rest.exchange(link, HttpMethod.GET, request, String.class); - if (rsp.getStatusCode() != HttpStatus.OK) { + XmlObject xml = new XmlObject("vlan").putElement("vlan-id", + new XmlObject("vlan-id").setText(String.valueOf(struct.getVlan()))).putElement("untagged", + new XmlObject("untagged").putElement(port.interfaceType, new XmlObject(port.interfaceType) + .putElement("name", new XmlObject("name").setText(port.port))) + ).putElement("shutdown", new XmlObject("shutdown").setText("false")); + request = new HttpEntity<>(xml.dump(), headers); + link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/")); + logger.debug(String.format("http get: %s, body: %s", link, request)); + rsp = rest.exchange(link, HttpMethod.POST, request, String.class); + if (!successHttpStatusCode.contains(rsp.getStatusCode())) { throw new CloudRuntimeException(String.format("unable to create vlan[%s] on force10 switch[ip:%s]. HTTP status code:%s, body dump:%s", - struct.getVlan(), rsp.getStatusCode(), struct.getSwitchIp(), rsp.getBody())); + struct.getVlan(), struct.getSwitchIp(),rsp.getStatusCode(), rsp.getBody())); + } else { + logger.debug(String.format("successfully programmed vlan[%s] on Force10[ip:%s, port:%s]. http response[status code:%s, body:%s]", + struct.getVlan(), struct.getSwitchIp(), struct.getPort(), rsp.getStatusCode(), rsp.getBody())); } - } else if (rsp.getStatusCode() == HttpStatus.OK) { + } else if (successHttpStatusCode.contains(rsp.getStatusCode())) { PortInfo port = new PortInfo(struct); XmlObject xml = XmlObjectParser.parseFromString((String)rsp.getBody()); - List<XmlObject> ports = xml.getAsList("tagged.tengigabitethernet"); - ports.addAll(xml.<XmlObject>getAsList("tagged.gigabitethernet")); - ports.addAll(xml.<XmlObject>getAsList("tagged.fortyGigE")); + List<XmlObject> ports = xml.getAsList("untagged.tengigabitethernet"); + ports.addAll(xml.<XmlObject>getAsList("untagged.gigabitethernet")); + ports.addAll(xml.<XmlObject>getAsList("untagged.fortyGigE")); for (XmlObject pxml : ports) { XmlObject name = pxml.get("name"); if (port.port.equals(name.getText())) { @@ -98,14 +131,26 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { } } - XmlObject tag = xml.get("tagged"); - tag.putElement(port.interfaceType, new XmlObject("name").setText(port.port)); - request = new HttpEntity<>(xml.toString(), headers); - link = buildLink(struct.getSwitchIp(), struct.getVlan()); + xml.removeElement("mtu"); + xml.setText(null); + XmlObject tag = xml.get("untagged"); + if (tag == null) { + tag = new XmlObject("untagged"); + xml.putElement("untagged", tag); + } + + tag.putElement(port.interfaceType, new XmlObject(port.interfaceType) + .putElement("name", new XmlObject("name").setText(port.port))); + request = new HttpEntity<>(xml.dump(), headers); + link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/vlan/%s", struct.getVlan())); + logger.debug(String.format("http get: %s, body: %s", link, request)); rsp = rest.exchange(link, HttpMethod.PUT, request, String.class); - if (rsp.getStatusCode() != HttpStatus.NO_CONTENT) { + if (!successHttpStatusCode.contains(rsp.getStatusCode())) { throw new CloudRuntimeException(String.format("failed to program vlan[%s] for port[%s] on force10[ip:%s]. http status:%s, body dump:%s", struct.getVlan(), struct.getPort(), struct.getSwitchIp(), rsp.getStatusCode(), rsp.getBody())); + } else { + logger.debug(String.format("successfully join port[%s] into vlan[%s] on Force10[ip:%s]. http response[status code:%s, body:%s]", + struct.getPort(), struct.getVlan(), struct.getSwitchIp(), rsp.getStatusCode(), rsp.getBody())); } } else { throw new CloudRuntimeException(String.format("force10[ip:%s] returns unexpected error[%s] when http getting %s, body dump:%s", @@ -115,18 +160,19 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { @Override public void removePortFromVlan(BaremetalVlanStruct struct) { - String link = buildLink(struct.getSwitchIp(), struct.getVlan()); + String link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/vlan/%s", struct.getVlan())); HttpHeaders headers = createBasicAuthenticationHeader(struct); HttpEntity<String> request = new HttpEntity<>(headers); + logger.debug(String.format("http get: %s, body: %s", link, request)); ResponseEntity rsp = rest.exchange(link, HttpMethod.GET, request, String.class); if (rsp.getStatusCode() == HttpStatus.NOT_FOUND) { logger.debug(String.format("vlan[%s] has been deleted on force10[ip:%s], no need to remove the port[%s] anymore", struct.getVlan(), struct.getSwitchIp(), struct.getPort())); } else if (rsp.getStatusCode() == HttpStatus.OK) { PortInfo port = new PortInfo(struct); XmlObject xml = XmlObjectParser.parseFromString((String)rsp.getBody()); - List<XmlObject> ports = xml.getAsList("tagged.tengigabitethernet"); - ports.addAll(xml.<XmlObject>getAsList("tagged.gigabitethernet")); - ports.addAll(xml.<XmlObject>getAsList("tagged.fortyGigE")); + List<XmlObject> ports = xml.getAsList("untagged.tengigabitethernet"); + ports.addAll(xml.<XmlObject>getAsList("untagged.gigabitethernet")); + ports.addAll(xml.<XmlObject>getAsList("untagged.fortyGigE")); List<XmlObject> newPorts = new ArrayList<>(); boolean needRemove = false; for (XmlObject pxml : ports) { @@ -143,11 +189,19 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { return; } - xml.putElement("tagged", newPorts); - - request = new HttpEntity<>(xml.toString(), headers); + xml.setText(null); + xml.removeElement("mtu"); + XmlObject tagged = xml.get("untagged"); + tagged.removeAllChildren(); + for (XmlObject p : newPorts) { + tagged.putElement(p.getTag(), p); + } + + + request = new HttpEntity<>(xml.dump(), headers); + logger.debug(String.format("http get: %s, body: %s", link, request)); rsp = rest.exchange(link, HttpMethod.PUT, request, String.class); - if (rsp.getStatusCode() != HttpStatus.NO_CONTENT) { + if (!successHttpStatusCode.contains(rsp.getStatusCode())) { throw new CloudRuntimeException(String.format("failed to program vlan[%s] for port[%s] on force10[ip:%s]. http status:%s, body dump:%s", struct.getVlan(), struct.getPort(), struct.getSwitchIp(), rsp.getStatusCode(), rsp.getBody())); } else { @@ -166,6 +220,8 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { String base64Creds = new String(base64CredsBytes); HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", "Basic " + base64Creds); + headers.setAccept(Arrays.asList(MediaType.ALL)); + headers.setContentType(MediaType.valueOf("application/vnd.yang.data+xml")); return headers; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java new file mode 100755 index 0000000..4f59a83 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java @@ -0,0 +1,83 @@ +// 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. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import com.cloud.agent.api.Command; + +public class PrepareKickstartPxeServerCommand extends Command { + private String ksFile; + private String templateUuid; + private String mac; + private String ksDevice; + private String kernel; + private String initrd; + + @Override + public boolean executeInSequence() { + return true; + } + + public String getKsFile() { + return ksFile; + } + + public void setKsFile(String ksFile) { + this.ksFile = ksFile; + } + + public String getKernel() { + return kernel; + } + + public void setKernel(String kernel) { + this.kernel = kernel; + } + + public String getInitrd() { + return initrd; + } + + public void setInitrd(String initrd) { + this.initrd = initrd; + } + + public String getTemplateUuid() { + return templateUuid; + } + + public void setTemplateUuid(String templateUuid) { + this.templateUuid = templateUuid; + } + + public String getMac() { + return mac; + } + + public void setMac(String mac) { + this.mac = mac; + } + + public String getKsDevice() { + return ksDevice; + } + + public void setKsDevice(String ksDevice) { + this.ksDevice = ksDevice; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/plugins/hypervisors/baremetal/src/org/apache/cloudstack/api/BaremetalProvisionDoneNotificationCmd.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/baremetal/src/org/apache/cloudstack/api/BaremetalProvisionDoneNotificationCmd.java b/plugins/hypervisors/baremetal/src/org/apache/cloudstack/api/BaremetalProvisionDoneNotificationCmd.java new file mode 100755 index 0000000..a5248aa --- /dev/null +++ b/plugins/hypervisors/baremetal/src/org/apache/cloudstack/api/BaremetalProvisionDoneNotificationCmd.java @@ -0,0 +1,87 @@ +// 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.api; + +import com.cloud.baremetal.manager.BaremetalManager; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; +import org.apache.log4j.Logger; + +/** + * Created by frank on 9/17/14. + */ +@APICommand(name = "notifyBaremetalProvisionDone", description = "Notify provision has been done on a host. This api is for baremetal virtual router service, not for end user", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.User}) +public class BaremetalProvisionDoneNotificationCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(BaremetalProvisionDoneNotificationCmd.class); + private static final String s_name = "baremetalprovisiondone"; + + @Inject + private BaremetalManager bmMgr; + + @Parameter(name="mac", required = true, description = "mac of the nic used for provision") + private String mac; + + @Override + public String getEventType() { + return EventTypes.EVENT_BAREMETAL_PROVISION_DONE; + } + + @Override + public String getEventDescription() { + return "notify management server that baremetal provision has been done on a host"; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + bmMgr.notifyProvisionDone(this); + this.setResponseObject(new SuccessResponse(getCommandName())); + } catch (Exception e) { + s_logger.warn(String.format("unable to notify baremetal provision done[mac:%s]", mac), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + public String getMac() { + return mac; + } + + public void setMac(String mac) { + this.mac = mac; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/server/src/com/cloud/configuration/Config.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 71f5c5b..3710e0a 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -1848,29 +1848,37 @@ public enum Config { "The maximum number of retrying times to search for an available IPv6 address in the table", null), - BaremetalEnableCompleteNotification( + BaremetalInternalStorageServer( + "Advanced", + ManagementServer.class, + String.class, + "baremetal.internal.storage.server.ip", + null, + "the ip address of server that stores kickstart file, kernel, initrd, ISO for advanced networking baremetal provisioning", + null), + BaremetalProvisionDoneNotificationEnabled( "Advanced", ManagementServer.class, Boolean.class, - "baremetal.provision.done.notification", - "false", - "Enable provision done notification through virtual router", + "baremetal.provision.done.notification.enabled", + "true", + "whether to enable baremetal provison done notification", null), - BaremetalPeerHypervisorType( + BaremetalProvisionDoneNotificationTimeout( "Advanced", ManagementServer.class, - String.class, - "baremetal.peer.hypervisor.type", - "Vmware", - "Hypervisor[Xenserver/KVM/VMWare] used to spring up virtual router for baremetal instances. The cluster having this hypervisor type must be in the same zone with baremetal cluster", + Integer.class, + "baremetal.provision.done.notification.timeout", + "1800", + "the max time to wait before treating a baremetal provision as failure if no provision done notification is not received, in secs", null), - BaremetalInternalStorageServer( + BaremetalProvisionDoneNotificationPort( "Advanced", ManagementServer.class, - String.class, - "baremetal.internal.storage.server.ip", - null, - "the ip address of server that stores kickstart file, kernel, initrd, ISO for advanced networking baremetal provisioning", + Integer.class, + "baremetal.provision.done.notification.port", + "8080", + "the port that listens baremetal provision done notification. Should be the same to port management server listening on for now. Please change it to management server port if it's not default 8080", null), ExternalBaremetalSystemUrl( "Advanced", http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 3329ff2..bfb33b0 100755 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -404,6 +404,15 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> { s_logger.debug("The last host of this VM is UP and has enough capacity"); s_logger.debug("Now checking for suitable pools under zone: " + host.getDataCenterId() + ", pod: " + host.getPodId() + ", cluster: " + host.getClusterId()); + + Pod pod = _podDao.findById(host.getPodId()); + Cluster cluster = _clusterDao.findById(host.getClusterId()); + if (vm.getHypervisorType() == HypervisorType.BareMetal) { + DeployDestination dest = new DeployDestination(dc, pod, cluster, host, new HashMap<Volume, StoragePool>()); + s_logger.debug("Returning Deployment Destination: " + dest); + return dest; + } + // search for storage under the zone, pod, cluster // of // the last host. @@ -423,8 +432,6 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> { suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes); if (potentialResources != null) { - Pod pod = _podDao.findById(host.getPodId()); - Cluster cluster = _clusterDao.findById(host.getClusterId()); Map<Volume, StoragePool> storageVolMap = potentialResources.second(); // remove the reused vol<->pool from // destination, since we don't have to http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index be7cb37..315bdde 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1506,6 +1506,8 @@ Configurable, StateListener<State, VirtualMachine.Event, VirtualMachine> { } else { buf.append(String.format(" baremetalnotificationsecuritykey=%s", user.getSecretKey())); buf.append(String.format(" baremetalnotificationapikey=%s", user.getApiKey())); + buf.append(" host=").append(ApiServiceConfiguration.ManagementHostIPAdr.value()); + buf.append(" port=").append(_configDao.getValue(Config.BaremetalProvisionDoneNotificationPort.key())); } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/server/src/com/cloud/resource/ResourceManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index cdad91e..c7ba80a 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -1631,21 +1631,6 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, clusterId = c.getId(); } } - if (startup instanceof StartupRoutingCommand) { - StartupRoutingCommand ssCmd = ((StartupRoutingCommand)startup); - List<String> implicitHostTags = ssCmd.getHostTags(); - if (!implicitHostTags.isEmpty()) { - if (hostTags == null) { - hostTags = _hostTagsDao.gethostTags(host.getId()); - } - if (hostTags != null) { - implicitHostTags.removeAll(hostTags); - hostTags.addAll(implicitHostTags); - } else { - hostTags = implicitHostTags; - } - } - } if (startup instanceof StartupRoutingCommand) { StartupRoutingCommand ssCmd = ((StartupRoutingCommand)startup); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/server/src/com/cloud/storage/VolumeApiServiceImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 9f132c3..e089b77 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -1230,10 +1230,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic // Check that the volume ID is valid VolumeInfo volumeToAttach = volFactory.getVolume(volumeId); - // Check that the volume is a data volume - if (volumeToAttach == null || !(volumeToAttach.getVolumeType() == Volume.Type.DATADISK)) { - throw new InvalidParameterValueException("Please specify a volume with the valid type: " + Volume.Type.DATADISK.toString()); + if (volumeToAttach == null || !(volumeToAttach.getVolumeType() == Volume.Type.DATADISK || volumeToAttach.getVolumeType() == Volume.Type.ROOT)) { + throw new InvalidParameterValueException("Please specify a volume with the valid type: " + Volume.Type.ROOT.toString() + " or " + Volume.Type.DATADISK.toString()); } // Check that the volume is not currently attached to any VM @@ -1516,10 +1515,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); } - // Check that the volume is a data volume. - // TODO - Disabling root volume detach for now, enable it back in 4.6 - if (volume.getVolumeType() != Volume.Type.DATADISK) { - throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK.toString()); + // Check that the volume is a data/root volume + if (!(volume.getVolumeType() == Volume.Type.ROOT || volume.getVolumeType() == Volume.Type.DATADISK)) { + throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK.toString() + " or " + Volume.Type.ROOT.toString()); } // Root volume detach is allowed for following hypervisors: Xen/KVM/VmWare http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/server/test/com/cloud/storage/VolumeApiServiceImplTest.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/storage/VolumeApiServiceImplTest.java b/server/test/com/cloud/storage/VolumeApiServiceImplTest.java index 3d2de4e..c60aa50 100644 --- a/server/test/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/test/com/cloud/storage/VolumeApiServiceImplTest.java @@ -30,6 +30,7 @@ import javax.inject.Inject; import org.junit.After; import org.junit.Before; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mock; import org.mockito.Mockito; @@ -48,6 +49,7 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; @@ -241,7 +243,7 @@ public class VolumeApiServiceImplTest { * @throws Exception */ - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void testDetachVolumeFromRunningVm() throws NoSuchFieldException, IllegalAccessException { Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); dedicateIdField.setAccessible(true); @@ -249,7 +251,7 @@ public class VolumeApiServiceImplTest { _svc.detachVolumeFromVM(detachCmd); } - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void testDetachVolumeFromStoppedHyperVVm() throws NoSuchFieldException, IllegalAccessException { Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); dedicateIdField.setAccessible(true); @@ -257,7 +259,7 @@ public class VolumeApiServiceImplTest { _svc.detachVolumeFromVM(detachCmd); } - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void testDetachVolumeOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException { Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); dedicateIdField.setAccessible(true); @@ -268,7 +270,7 @@ public class VolumeApiServiceImplTest { @Rule public ExpectedException thrown = ExpectedException.none(); - //@Test + @Test public void testDetachVolumeFromStoppedXenVm() throws NoSuchFieldException, IllegalAccessException { thrown.expect(NullPointerException.class); Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); @@ -282,43 +284,43 @@ public class VolumeApiServiceImplTest { */ // Negative test - try to attach non-root non-datadisk volume - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void attachIncorrectDiskType() throws NoSuchFieldException, IllegalAccessException { _svc.attachVolumeToVM(1L, 5L, 0L); } // Negative test - attach root volume to running vm - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void attachRootDiskToRunningVm() throws NoSuchFieldException, IllegalAccessException { _svc.attachVolumeToVM(1L, 6L, 0L); } // Negative test - attach root volume to non-xen vm - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void attachRootDiskToHyperVm() throws NoSuchFieldException, IllegalAccessException { _svc.attachVolumeToVM(3L, 6L, 0L); } // Negative test - attach root volume from the managed data store - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void attachRootDiskOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException { _svc.attachVolumeToVM(2L, 7L, 0L); } // Negative test - root volume can't be attached to the vm already having a root volume attached - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void attachRootDiskToVmHavingRootDisk() throws NoSuchFieldException, IllegalAccessException { _svc.attachVolumeToVM(4L, 6L, 0L); } // Negative test - root volume in uploaded state can't be attached - //@Test(expected = InvalidParameterValueException.class) + @Test(expected = InvalidParameterValueException.class) public void attachRootInUploadedState() throws NoSuchFieldException, IllegalAccessException { _svc.attachVolumeToVM(2L, 8L, 0L); } // Positive test - attach ROOT volume in correct state, to the vm not having root volume attached - //@Test + @Test public void attachRootVolumePositive() throws NoSuchFieldException, IllegalAccessException { thrown.expect(NullPointerException.class); _svc.attachVolumeToVM(2L, 6L, 0L); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/systemvm/patches/debian/config/etc/dnsmasq.conf.tmpl ---------------------------------------------------------------------- diff --git a/systemvm/patches/debian/config/etc/dnsmasq.conf.tmpl b/systemvm/patches/debian/config/etc/dnsmasq.conf.tmpl index 447e748..28b598c 100644 --- a/systemvm/patches/debian/config/etc/dnsmasq.conf.tmpl +++ b/systemvm/patches/debian/config/etc/dnsmasq.conf.tmpl @@ -415,7 +415,7 @@ dhcp-option=vendor:MSFT,2,1i # this is you want to boot machines over the network and you will need # a TFTP server; either dnsmasq's built in TFTP server or an # external one. (See below for how to enable the TFTP server.) -#dhcp-boot=pxelinux.0 +dhcp-boot=pxelinux.0 # The same as above, but use custom tftp-server instead machine running dnsmasq #dhcp-boot=pxelinux,server.name,192.168.1.100 @@ -472,10 +472,10 @@ dhcp-option=vendor:MSFT,2,1i # Enable dnsmasq's built-in TFTP server -#enable-tftp +enable-tftp # Set the root directory for files available via FTP. -#tftp-root=/var/ftpd +tftp-root=/opt/tftpboot # Make the TFTP server more secure: with this set, only files owned by # the user dnsmasq is running as will be send over the net. http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh ---------------------------------------------------------------------- diff --git a/systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh b/systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh index f35a16f..22e5669 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh +++ b/systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh @@ -21,11 +21,12 @@ set +u mgmt_nic_ip=$1 internal_server_ip=$2 +gateway_ip=$3 ip route | grep "$internal_server_ip" > /dev/null if [ $? -ne 0 ]; then - ip route add $internal_server_ip via $mgmt_nic_ip + ip route add $internal_server_ip via $gateway_ip fi iptables-save | grep -- "-A POSTROUTING -d $internal_server_ip" > /dev/null @@ -33,3 +34,21 @@ iptables-save | grep -- "-A POSTROUTING -d $internal_server_ip" > /dev/null if [ $? -ne 0 ]; then iptables -t nat -A POSTROUTING -d $internal_server_ip -j SNAT --to-source $mgmt_nic_ip fi + + +iptables-save | grep -- "-A INPUT -i eth0 -p udp -m udp --dport 69 -j ACCEPT" > /dev/null +if [ $? -ne 0 ]; then + iptables -I INPUT -i eth0 -p udp -m udp --dport 69 -j ACCEPT +fi + +iptables-save | grep -- "-A FORWARD -i eth1 -o eth0 -j ACCEPT" > /dev/null +if [ $? -ne 0 ]; then + iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT +fi + +rule="-A FORWARD -d $internal_server_ip/32 -i eth0 -o eth1 -j ACCEPT" +iptables-save | grep -- "$rule" > /dev/null +if [ $? -ne 0 ]; then + iptables -I FORWARD -d $internal_server_ip/32 -i eth0 -o eth1 -j ACCEPT +fi + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh ---------------------------------------------------------------------- diff --git a/systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh b/systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh index bd9ece6..5bc1a93 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh +++ b/systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh @@ -40,6 +40,20 @@ tmpt_uuid=$3 pxe_cfg_filename=$4 ks_file=$5 +kernel_path=$tmpt_uuid/$kernel_file_name +initrd_path=$tmpt_uuid/$initrd_file_name + +cat > $PXELINUX_CFG_DIR/$pxe_cfg_filename <<EOF +DEFAULT default +PROMPT 1 +TIMEOUT 26 +DISPLAY boot.msg +LABEL default +KERNEL $kernel_path +APPEND ramdisk_size=66000 initrd=$initrd_path ks=$ks_file + +EOF + tmpt_dir=$TFTP_ROOT/$tmpt_uuid if [ -d $tmpt_dir ]; then success @@ -47,9 +61,6 @@ fi mkdir -p $tmpt_dir -kernel_path=$tmpt_uuid/$kernel_file_name -initrd_path=$tmpt_uuid/$initrd_file_name - mnt_path=/tmp/$(uuid) mkdir -p $mnt_path @@ -61,17 +72,6 @@ mount `dirname $initrd_nfs_path` $mnt_path cp -f $mnt_path/$initrd_file_name $tmpt_dir/$initrd_file_name umount $mnt_path -cat > $PXELINUX_CFG_DIR/$pxe_cfg_filename <<EOF -DEFAULT default -PROMPT 1 -TIMEOUT 26 -DISPLAY boot.msg -LABEL default -KERNEL $kernel_path -APPEND ramdisk_size=66000 initrd=$initrd_path ks=$ks_file - -EOF - success http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/test/integration/component/test_deploy_vgpu_vm.py ---------------------------------------------------------------------- diff --git a/test/integration/component/test_deploy_vgpu_vm.py b/test/integration/component/test_deploy_vgpu_vm.py index 6cd6c2c..0284eab 100644 --- a/test/integration/component/test_deploy_vgpu_vm.py +++ b/test/integration/component/test_deploy_vgpu_vm.py @@ -1186,282 +1186,6 @@ class TestvGPUWindowsVm(cloudstackTestCase): - def new_template_register(self,guestostype): - - template1 = get_windows_template(self.apiclient, self.zone.id ,ostype_desc=guestostype) - - if template1 == FAILED: - if "http://pleaseupdateURL/dummy.vhd"; in cls.testdata["vgpu"] [guestostype]["url"]: - raise unittest.SkipTest("Check Test Data file if it has the valid template URL") - template1 = Template.register( - self.apiclient, - self.testdata["vgpu"] [guestostype], - hypervisor = "XenServer", - zoneid=cls.zone.id, - domainid=cls.account.domainid, - account=cls.account.name - ) - timeout = self.testdata["vgpu"]["timeout"] - - while True: - time.sleep(self.testdata["vgpu"]["sleep"]) - list_template_response = Template.list( - self.apiclient, - templatefilter=\ - self.testdata["templatefilter"], - id=template1.id - ) - if (isinstance(list_template_response, list)) is not True: - raise unittest.SkipTest("Check list template api response returns a valid list") - - if len(list_template_response) is None : - raise unittest.SkipTest("Check template registered is in List Templates") - - template_response = list_template_response[0] - if template_response.isready == True: - break - if timeout == 0: - raise unittest.SkipTest("Failed to download template(ID: %s)" % template_response.id) - - timeout = timeout - 1 - return(template1.id) - - def deploy_vm_lifecycle(self): - """ - Create Service Offerings for Both K1 and K2 cards to be used for VM life cycle tests - """ - - if(self.k1hosts != 0): - if(self.k140qgpuhosts != 0): - gtype = "GRID K140Q" - elif(self.k120qgpuhosts != 0): - gtype = "GRID K120Q" - elif(self.k100gpuhosts !=0): - gtype = "GRID K100" - else: - gtype = "passthrough" - - self.testdata["vgpu"]["service_offerings"][gtype]["serviceofferingdetails"] = [{'pciDevice':'Group of NVIDIA Corporation GK107GL [GRID K1] GPUs'}, - {'vgpuType':gtype}] - try: - self.__class__.k100_vgpu_service_offering = ServiceOffering.create( - self.apiclient, - self.testdata["vgpu"]["service_offerings"][gtype] - ) - except Exception as e: - self.fail("Failed to create the service offering, %s" % e) - - if(self.k2hosts != 0): - if(self.k240qgpuhosts != 0): - gtype = "GRID K240Q" - elif(cls.k220qgpuhosts != 0): - gtype = "GRID K220Q" - elif(self.k200gpuhosts !=0): - gtype = "GRID K200" - else: - gtype = "passthrough" - - self.testdata["vgpu"]["service_offerings"][gtype]["serviceofferingdetails"] = [{'pciDevice': 'Group of NVIDIA Corporation GK104GL [GRID K2] GPUs'}, - {'vgpuType':gtype}] - try: - self.__class__.k200_vgpu_service_offering = ServiceOffering.create( - self.apiclient, - self.testdata["vgpu"]["service_offerings"][gtype] - ) - except Exception as e: - self.fail("Failed to create the service offering, %s" % e) - - win8templateid=self.new_template_register("Windows 8 (64-bit)") - win2012templateid=self.new_template_register("Windows Server 2012 (64-bit)") - win7templateid=self.new_template_register("Windows 7 (64-bit)") - - """ - Create Virtual Machines for Both K1 and K2 cards to be used for VM life cycle tests - """ - - if(self.k1hosts != 0): - self.__class__.vm_k1_card = VirtualMachine.create( - self.apiclient, - self.testdata["virtual_machine"], - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - serviceofferingid=self.k100_vgpu_service_offering.id, - templateid=win8templateid - ) - if(self.k2hosts !=0): - self.__class__.vm_k2_card = VirtualMachine.create( - self.apiclient, - self.testdata["virtual_machine"], - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - serviceofferingid=self.k200_vgpu_service_offering.id, - templateid=win2012templateid - ) - if(self.k2hosts !=0): - self.__class__.vm2_k2_card = VirtualMachine.create( - self.apiclient, - self.testdata["virtual_machine"], - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - serviceofferingid=self.k200_vgpu_service_offering.id, - templateid=win7templateid - ) - return - - - def check_gpu_resources_released_vm(self,gpuhostid,vm_vgpu_type,rcapacity): - hhosts = list_hosts( - self.apiclient, - hypervisor="XenServer", - id=gpuhostid - ) - self.assertEqual( - isinstance(hhosts, list), - True, - "Check list hosts response returns a valid list" - ) - - self.assertNotEqual( - len(hhosts), - 0, - "Check Host details are available in List Hosts" - ) - for ggroup in hhosts: - if ggroup.ipaddress not in self.nongpuhosts: - for gp in ggroup.gpugroup: - #if gp.gpugroupname == "Group of NVIDIA Corporation GK104GL [GRID K2] GPUs": - for gptype in gp.vgpu: - if gptype.vgputype==vm_vgpu_type: - self.debug("Latest remainingcapacity is %s and before remainingcapacity is %s"%(gptype.remainingcapacity,rcapacity)) - if gptype.remainingcapacity != rcapacity+1: - self.fail("Host capacity is not updated .GPU resources should be released when VM is stopped/Destroyed ") - return - - def check_vm_state(self,vmid): - list_vm_response = list_virtual_machines( - self.apiclient, - id=vmid - ) - - if list_vm_response is None: - return("Expunge") - return(list_vm_response[0].state) - - def check_host_vgpu_remaining_capacity(self,gpuhostid,gtype): - gputhosts = list_hosts( - self.apiclient, - hypervisor="XenServer", - id=gpuhostid - ) - vgpucapacity=0 - for ghost in gputhosts: - if ghost.gpugroup is not None: - for gp in ghost.gpugroup: - #if gp.gpugroupname == gpucard: - for gptype in gp.vgpu: - if gptype.vgputype == gtype: - vgpucapacity=vgpucapacity+gptype.remainingcapacity - - return(vgpucapacity) - - def verify_vm(self,vm_gpu_card): - if(vm_gpu_card): - vm_gpu_card.getState( - self.apiclient, - "Running") - - self.check_for_vGPU_resource(vm_gpu_card.hostid,vm_gpu_card.instancename,vm_gpu_card.serviceofferingid,vm_gpu_card.vgpu) - - def stop_life_cycle_vm (self,vm_gpu_card): - - if(vm_gpu_card): - vm_gpu_card.stop(self.apiclient) - time.sleep(self.testdata["vgpu"]["sleep"]) - vm_gpu_card.getState( - self.apiclient, - "Stopped") - - def start_life_cycle_vm(self,vm_gpu_card): - - if(vm_gpu_card): - vm_gpu_card.start(self.apiclient) - time.sleep(self.testdata["vgpu"]["sleep"]) - - def restore_life_cycle_vm(self,vm_gpu_card): - if(vm_gpu_card): - vm_gpu_card.restore(self.apiclient) - time.sleep(self.testdata["vgpu"]["sleep"]) - - def reboot_life_cycle_vm(self,vm_gpu_card): - - if(vm_gpu_card): - vm_gpu_card.reboot(self.apiclient) - time.sleep(self.testdata["vgpu"]["sleep"]) - - def delete_vm_life_cycle_vm(self,vm_gpu_card): - if(vm_gpu_card): - vm_gpu_card.delete(self.apiclient) - time.sleep(self.testdata["vgpu"]["sleep"]) - vm_gpu_card.getState( - self.apiclient, - "Destroyed") - - def recover_vm_life_cycle_vm(self,vm_gpu_card): - if(vm_gpu_card): - vm_gpu_card.recover(self.apiclient) - vm_gpu_card.getState( - self.apiclient, - "Stopped") - - - def recovervm(self,vm_gpu_card): - if self.check_vm_state(vm_gpu_card.id)=="Expunge": - raise unittest.SkipTest("VM is already deleted hence skipping") - self.recover_vm_life_cycle_vm(vm_gpu_card) - self.start_life_cycle_vm(vm_gpu_card) - self.verify_vm(vm_gpu_card) - return - - - def startvm(self,vm_gpu_card): - - self.start_life_cycle_vm(vm_gpu_card) - self.verify_vm(vm_gpu_card) - return - - def stopvm(self,vm_gpu_card): - - rcapacity=self.check_host_vgpu_remaining_capacity(vm_gpu_card.hostid,vm_gpu_card.vgpu) - self.stop_life_cycle_vm(vm_gpu_card) - self.check_gpu_resources_released_vm(vm_gpu_card.hostid,vm_gpu_card.vgpu,rcapacity) - return - - def deletevm(self,vm_gpu_card): - - rcapacity=self.check_host_vgpu_remaining_capacity(vm_gpu_card.hostid,vm_gpu_card.vgpu) - hostid=vm_gpu_card.hostid - vgputype=vm_gpu_card.vgpu - self.delete_vm_life_cycle_vm(vm_gpu_card) - self.check_gpu_resources_released_vm(hostid,vgputype,rcapacity) - return - - def restorevm(self,vm_gpu_card): - - self.restore_life_cycle_vm(vm_gpu_card) - self.verify_vm(vm_gpu_card) - return - - - def rebootvm(self,vm_vgpu_card): - - self.reboot_life_cycle_vm(vm_vgpu_card) - self.verify_vm(vm_vgpu_card) - return - - def test_01_list_vgpu_host_details(self): """ list vGPU host details """ hhosts = list_hosts( @@ -1770,6 +1494,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): self.debug("Check if deployed VMs are in running state?") + if self.__class__.vm_k1_card is not None: self.verify_vm(self.__class__.vm_k1_card) @@ -1779,6 +1504,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): if self.__class__.vm2_k2_card is not None: self.verify_vm(self.__class__.vm2_k2_card) + self.__class__.vmlifecycletest=1 return @@ -1789,6 +1515,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): if self.__class__.vmlifecycletest==0: raise unittest.SkipTest("VM Life Cycle Deploy VM test failed hence skipping") + if self.__class__.vm_k1_card: self.stopvm(self.__class__.vm_k1_card) @@ -1798,6 +1525,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): if self.__class__.vm2_k2_card: self.stopvm(self.__class__.vm2_k2_card) + return @attr(tags = ['advanced', 'basic' , 'vgpu'], required_hardware="true") @@ -1807,6 +1535,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): if self.__class__.vmlifecycletest==0: raise unittest.SkipTest("VM Life Cycle Deploy VM test failed hence skipping") + if self.__class__.vm_k1_card: self.startvm(self.__class__.vm_k1_card) @@ -1826,6 +1555,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): if self.__class__.vmlifecycletest==0: raise unittest.SkipTest("VM Life Cycle Deploy VM test failed hence skipping") + if self.__class__.vm_k1_card: self.restorevm(self.__class__.vm_k1_card) if self.__class__.vm_k2_card: @@ -1833,6 +1563,7 @@ class TestvGPUWindowsVm(cloudstackTestCase): if self.__class__.vm2_k2_card: self.restorevm(self.__class__.vm2_k2_card) + return @attr(tags = ['advanced', 'basic' , 'vgpu'], required_hardware="true") @@ -2052,11 +1783,6 @@ class TestvGPUWindowsVm(cloudstackTestCase): self.deletevm(self.__class__.nonvgpu) self.cleanup.append(self.__class__.nonvgpu_service_offerin) - - self.deletevm(self.__class__.vm_k1_card) - self.deletevm(self.__class__.vm_k2_card) - self.deletevm(self.__class__.vm2_k2_card) - self.cleanup.append(self.__class__.k100_vgpu_service_offering) self.cleanup.append(self.__class__.k200_vgpu_service_offering) @@ -2233,3 +1959,6 @@ class TestvGPUWindowsVm(cloudstackTestCase): except Exception as e: self.debug("Warning! Exception in tearDown: %s" % e) + + + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/utils/src/com/cloud/utils/xmlobject/XmlObject.java ---------------------------------------------------------------------- diff --git a/utils/src/com/cloud/utils/xmlobject/XmlObject.java b/utils/src/com/cloud/utils/xmlobject/XmlObject.java index e4e4363..42af945 100755 --- a/utils/src/com/cloud/utils/xmlobject/XmlObject.java +++ b/utils/src/com/cloud/utils/xmlobject/XmlObject.java @@ -27,10 +27,12 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.logging.Logger; import com.cloud.utils.exception.CloudRuntimeException; public class XmlObject { + private final Logger logger = Logger.getLogger(XmlObject.class.getName()); private final Map<String, Object> elements = new HashMap<String, Object>(); private String text; private String tag; @@ -38,6 +40,10 @@ public class XmlObject { XmlObject() { } + public void removeAllChildren() { + elements.clear(); + } + public XmlObject(String tag) { this.tag = tag; } @@ -99,8 +105,12 @@ public class XmlObject { if (e instanceof List) { return (List<T>)e; } + List lst = new ArrayList(1); - lst.add(e); + if (e != null) { + lst.add(e); + } + return lst; } @@ -142,7 +152,8 @@ public class XmlObject { } if (!children.isEmpty() && text != null) { - throw new CloudRuntimeException(String.format("element %s cannot have both text[%s] and child elements", tag, text)); + logger.info(String.format("element %s cannot have both text[%s] and child elements, set text to null", tag, text)); + text = null; } if (!children.isEmpty()) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java ---------------------------------------------------------------------- diff --git a/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java b/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java old mode 100644 new mode 100755 index 321af0c..f0e8ce3 --- a/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java +++ b/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java @@ -19,6 +19,13 @@ package com.cloud.utils.xmlobject; +import com.cloud.utils.exception.CloudRuntimeException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -26,15 +33,6 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Stack; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import com.cloud.utils.exception.CloudRuntimeException; - public class XmlObjectParser { final private InputStream is; @@ -109,7 +107,11 @@ public class XmlObjectParser { public static XmlObject parseFromString(String xmlString) { InputStream stream = new ByteArrayInputStream(xmlString.getBytes()); XmlObjectParser p = new XmlObjectParser(stream); - return p.parse(); + XmlObject obj = p.parse(); + if (obj.getText() != null && obj.getText().replaceAll("\\n", "").replaceAll("\\r", "").replaceAll(" ", "").isEmpty()) { + obj.setText(null); + } + return obj; } private XmlObject parse() { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1de0cb70/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java ---------------------------------------------------------------------- diff --git a/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java b/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java old mode 100644 new mode 100755 index cbd24b0..faaf980 --- a/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java +++ b/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java @@ -43,6 +43,11 @@ public class TestXmlObject { } } */ + + XmlObject xml = new XmlObject("vlan").putElement("vlan-id", String.valueOf(19)).putElement("tagged", + new XmlObject("teng").putElement("name", "0/0") + ).putElement("shutdown", "false"); + System.out.println(xml.toString()); } }