diff --git a/api/src/com/cloud/hypervisor/Hypervisor.java b/api/src/com/cloud/hypervisor/Hypervisor.java index 17178bd2154..52b2a414d0a 100644 --- a/api/src/com/cloud/hypervisor/Hypervisor.java +++ b/api/src/com/cloud/hypervisor/Hypervisor.java @@ -23,7 +23,10 @@ public class Hypervisor { None, //for storage hosts Xen, XenServer, - KVM; + KVM, + VmWare, + VirtualBox, + Parralels; } } diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index 3f6d9b1013e..c7a981eeb41 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -23,7 +23,7 @@ import com.cloud.user.OwnedBy; public interface Volume extends PartOf, OwnedBy, BasedOn { - enum VolumeType {UNKNOWN, ROOT, SWAP, DATADISK}; + enum VolumeType {UNKNOWN, ROOT, SWAP, DATADISK, ISO}; enum MirrorState {NOT_MIRRORED, ACTIVE, DEFUNCT}; diff --git a/api/src/com/cloud/template/VirtualMachineTemplate.java b/api/src/com/cloud/template/VirtualMachineTemplate.java index 8259206f52c..0cdffe856d8 100755 --- a/api/src/com/cloud/template/VirtualMachineTemplate.java +++ b/api/src/com/cloud/template/VirtualMachineTemplate.java @@ -21,7 +21,7 @@ import com.cloud.storage.Storage.FileSystem; public interface VirtualMachineTemplate { - public static enum BootloaderType { PyGrub, HVM, External }; + public static enum BootloaderType { PyGrub, HVM, External, CD }; /** * @return id. diff --git a/api/src/com/cloud/vm/NetworkConcierge.java b/api/src/com/cloud/vm/NetworkConcierge.java index 03ab6fae6df..92f99fdb2ed 100644 --- a/api/src/com/cloud/vm/NetworkConcierge.java +++ b/api/src/com/cloud/vm/NetworkConcierge.java @@ -17,7 +17,7 @@ import com.cloud.utils.component.Adapter; public interface NetworkConcierge extends Adapter { String getUniqueName(); - Pair reserve(long vmId, NetworkCharacteristics ch) throws InsufficientVirtualNetworkCapcityException; + Pair reserve(long vmId, NetworkCharacteristics ch) throws InsufficientVirtualNetworkCapcityException; boolean release(String uniqueName, String uniqueId); } diff --git a/build/build.number b/build/build.number index fd157380ba6..77edd644e70 100644 --- a/build/build.number +++ b/build/build.number @@ -1,3 +1,3 @@ #Build Number for ANT. Do not edit! -#Thu Aug 19 11:24:57 PDT 2010 -build.number=71 +#Fri Aug 20 13:33:49 PDT 2010 +build.number=80 diff --git a/core/src/com/cloud/agent/api/Start2Answer.java b/core/src/com/cloud/agent/api/Start2Answer.java new file mode 100644 index 00000000000..7b6ec6eb740 --- /dev/null +++ b/core/src/com/cloud/agent/api/Start2Answer.java @@ -0,0 +1,21 @@ +/** + * + */ +package com.cloud.agent.api; + +public class Start2Answer extends Answer { + protected Start2Answer() { + } + + public Start2Answer(Start2Command cmd, String msg) { + super(cmd, false, msg); + } + + public Start2Answer(Start2Command cmd, Exception e) { + super(cmd, false, e.getMessage()); + } + + public Start2Answer(Start2Command cmd) { + super(cmd, true, null); + } +} diff --git a/core/src/com/cloud/agent/api/Start2Command.java b/core/src/com/cloud/agent/api/Start2Command.java new file mode 100644 index 00000000000..4414b888d44 --- /dev/null +++ b/core/src/com/cloud/agent/api/Start2Command.java @@ -0,0 +1,62 @@ +/** + * + */ +package com.cloud.agent.api; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class Start2Command extends Command { + VirtualMachineTO vm; + + public VirtualMachineTO getVirtualMachine() { + return vm; + } + + /* + long id; + String guestIpAddress; + String gateway; + int ramSize; + String imagePath; + String guestNetworkId; + String guestMacAddress; + String vncPassword; + String externalVlan; + String externalMacAddress; + int utilization; + int cpuWeight; + int cpu; + int networkRateMbps; + int networkRateMulticastMbps; + String hostName; + String arch; + String isoPath; + boolean bootFromISO; + String guestOSDescription; + + ---->console proxy + private ConsoleProxyVO proxy; + private int proxyCmdPort; + private String vncPort; + private String urlPort; + private String mgmt_host; + private int mgmt_port; + private boolean sslEnabled; + + ----->abstract + protected String vmName; + protected String storageHosts[] = new String[2]; + protected List volumes; + protected boolean mirroredVols = false; + protected BootloaderType bootloader = BootloaderType.PyGrub; + + */ + + @Override + public boolean executeInSequence() { + return true; + } + + public Start2Command() { + } +} diff --git a/core/src/com/cloud/agent/api/to/HostTO.java b/core/src/com/cloud/agent/api/to/HostTO.java index 717412f4c1e..91cfa65437e 100644 --- a/core/src/com/cloud/agent/api/to/HostTO.java +++ b/core/src/com/cloud/agent/api/to/HostTO.java @@ -18,7 +18,6 @@ package com.cloud.agent.api.to; import com.cloud.host.HostVO; -import com.cloud.vm.NetworkTO; public class HostTO { private String guid; diff --git a/api/src/com/cloud/vm/NetworkTO.java b/core/src/com/cloud/agent/api/to/NetworkTO.java similarity index 60% rename from api/src/com/cloud/vm/NetworkTO.java rename to core/src/com/cloud/agent/api/to/NetworkTO.java index 844b1943e12..d6e428831f3 100644 --- a/api/src/com/cloud/vm/NetworkTO.java +++ b/core/src/com/cloud/agent/api/to/NetworkTO.java @@ -15,7 +15,10 @@ * along with this program. If not, see . * */ -package com.cloud.vm; +package com.cloud.agent.api.to; + +import com.cloud.network.Network.BroadcastDomainType; +import com.cloud.network.Network.TrafficType; /** * Transfer object to transfer network settings. @@ -28,11 +31,65 @@ public class NetworkTO { private String mac; private String dns1; private String dns2; - private String vlan; + private Long vlan; + private BroadcastDomainType broadcastType; + private TrafficType type; - protected NetworkTO() { + public NetworkTO() { } + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Long getVlan() { + return vlan; + } + + public void setVlan(Long vlan) { + this.vlan = vlan; + } + + public BroadcastDomainType getBroadcastType() { + return broadcastType; + } + + public void setBroadcastType(BroadcastDomainType broadcastType) { + this.broadcastType = broadcastType; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public void setNetmask(String netmask) { + this.netmask = netmask; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } + + public void setMac(String mac) { + this.mac = mac; + } + + public void setDns1(String dns1) { + this.dns1 = dns1; + } + + public void setDns2(String dns2) { + this.dns2 = dns2; + } + + public void setType(TrafficType type) { + this.type = type; + } + /** * This constructor is usually for hosts where the other information are not important. * @@ -56,7 +113,7 @@ public class NetworkTO { * @param dns1 * @param dns2 */ - public NetworkTO(String ip, String vlan, String netmask, String mac, String gateway, String dns1, String dns2) { + public NetworkTO(String ip, Long vlan, String netmask, String mac, String gateway, String dns1, String dns2) { this.ip = ip; this.netmask = netmask; this.mac = mac; @@ -89,4 +146,8 @@ public class NetworkTO { public String getDns2() { return dns2; } + + public TrafficType getType() { + return type; + } } diff --git a/core/src/com/cloud/agent/api/to/NicTO.java b/core/src/com/cloud/agent/api/to/NicTO.java new file mode 100644 index 00000000000..42e4f66bd94 --- /dev/null +++ b/core/src/com/cloud/agent/api/to/NicTO.java @@ -0,0 +1,36 @@ +/** + * + */ +package com.cloud.agent.api.to; + +public class NicTO extends NetworkTO { + int deviceId; + Integer controlPort; + Integer networkRateMbps; + Integer networkRateMulticastMbps; + + public NicTO() { + super(); + controlPort = null; + } + + public void setDeviceId(int deviceId) { + this.deviceId = deviceId; + } + + public int getDeviceId() { + return deviceId; + } + + public Integer getControlPort() { + return controlPort; + } + + public Integer getNetworkRateMbps() { + return networkRateMbps; + } + + public Integer getNetworkRateMulticastMbps() { + return networkRateMulticastMbps; + } +} diff --git a/core/src/com/cloud/agent/api/to/VirtualMachineTO.java b/core/src/com/cloud/agent/api/to/VirtualMachineTO.java new file mode 100644 index 00000000000..b37c2fd2426 --- /dev/null +++ b/core/src/com/cloud/agent/api/to/VirtualMachineTO.java @@ -0,0 +1,173 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.agent.api.to; + +import java.util.Map; + +import com.cloud.template.VirtualMachineTemplate.BootloaderType; +import com.cloud.vm.VirtualMachine.Type; + +public class VirtualMachineTO { + private long id; + private String name; + private BootloaderType bootloader; + Type type; + int cpus; + Integer weight; + Integer utilization; + long minRam; + long maxRam; + String hostName; + String arch; + String os; + String bootArgs; + String[] bootupScripts; + boolean rebootOnCrash; + + VolumeTO[] disks; + NicTO[] nics; + + public VirtualMachineTO() { + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Type getType() { + return type; + } + + public BootloaderType getBootloader() { + return bootloader; + } + + public void setBootloader(BootloaderType bootloader) { + this.bootloader = bootloader; + } + + public int getCpus() { + return cpus; + } + + public void setCpus(int cpus) { + this.cpus = cpus; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + + public Integer getUtilization() { + return utilization; + } + + public void setUtiliziation(Integer utilization) { + this.utilization = utilization; + } + + public long getMinRam() { + return minRam; + } + + public void setRam(long minRam, long maxRam) { + this.minRam = minRam; + this.maxRam = maxRam; + } + + public long getMaxRam() { + return maxRam; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + + public String getOs() { + return os; + } + + public void setOs(String os) { + this.os = os; + } + + public String getBootArgs() { + return bootArgs; + } + + public void setBootArgs(Map bootParams) { + StringBuilder buf = new StringBuilder(); + for (Map.Entry entry : bootParams.entrySet()) { + buf.append(" ").append(entry.getKey()).append("=").append(entry.getValue()); + } + bootArgs = buf.toString(); + } + + public String[] getBootupScripts() { + return bootupScripts; + } + + public void setBootupScripts(String[] bootupScripts) { + this.bootupScripts = bootupScripts; + } + + public VolumeTO[] getDisks() { + return disks; + } + + public void setDisks(VolumeTO[] disks) { + this.disks = disks; + } + + public NicTO[] getNetworks() { + return nics; + } + + public void setNics(NicTO[] nics) { + this.nics = nics; + } + +} diff --git a/core/src/com/cloud/agent/api/to/VmTO.java b/core/src/com/cloud/agent/api/to/VmTO.java deleted file mode 100644 index 5cacc391a1c..00000000000 --- a/core/src/com/cloud/agent/api/to/VmTO.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. - * - * This software is licensed under the GNU General Public License v3 or later. - * - * It is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.cloud.agent.api.to; - -import com.cloud.vm.NetworkTO; -import com.cloud.vm.VMInstanceVO; - -public class VmTO { - private long id; - private String name; - NetworkTO[] networks; - - public VmTO() { - } - - // FIXME: Preferrably NetworkTO is constructed inside the TO objects. - // But we're press for time so I'm going to let the conversion - // happen outside. - public VmTO(VMInstanceVO instance, NetworkTO[] networks) { - id = instance.getId(); - name = instance.getName(); - this.networks = networks; - } - -} diff --git a/core/src/com/cloud/agent/api/to/VolumeTO.java b/core/src/com/cloud/agent/api/to/VolumeTO.java index 39561f9d310..15853b13098 100644 --- a/core/src/com/cloud/agent/api/to/VolumeTO.java +++ b/core/src/com/cloud/agent/api/to/VolumeTO.java @@ -18,12 +18,11 @@ package com.cloud.agent.api.to; import com.cloud.storage.Storage; -import com.cloud.storage.Storage.StorageResourceType; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; -import com.cloud.storage.Storage.StoragePoolType; public class VolumeTO { @@ -39,6 +38,7 @@ public class VolumeTO { private Storage.StorageResourceType resourceType; private StoragePoolType storagePoolType; private long poolId; + private int deviceId; public VolumeTO(long id, Volume.VolumeType type, Storage.StorageResourceType resourceType, StoragePoolType poolType, String name, String mountPoint, String path, long size) { this.id = id; @@ -70,6 +70,10 @@ public class VolumeTO { this.storagePoolType = pool.getPoolType(); this.mountPoint = pool.getPath(); } + + public int getDeviceId() { + return deviceId; + } public Storage.StorageResourceType getResourceType() { return resourceType; diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index cf0a7932969..24997f1110a 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -104,6 +104,8 @@ import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RebootRouterCommand; import com.cloud.agent.api.SetupAnswer; import com.cloud.agent.api.SetupCommand; +import com.cloud.agent.api.Start2Answer; +import com.cloud.agent.api.Start2Command; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StartConsoleProxyAnswer; @@ -141,22 +143,26 @@ import com.cloud.agent.api.storage.DownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.agent.api.storage.ShareAnswer; import com.cloud.agent.api.storage.ShareCommand; +import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.StoragePoolTO; +import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.exception.InternalErrorException; import com.cloud.host.Host.Type; import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.Network.BroadcastDomainType; +import com.cloud.network.Network.TrafficType; import com.cloud.resource.ServerResource; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; -import com.cloud.storage.Storage.StorageResourceType; import com.cloud.storage.StorageLayer; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.Volume.VolumeType; import com.cloud.storage.VolumeVO; import com.cloud.storage.resource.StoragePoolResource; import com.cloud.storage.template.TemplateInfo; +import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; @@ -169,6 +175,7 @@ import com.cloud.vm.DiskCharacteristics; import com.cloud.vm.DomainRouter; import com.cloud.vm.SecondaryStorageVmVO; import com.cloud.vm.State; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineName; import com.trilead.ssh2.SCPClient; import com.xensource.xenapi.APIVersion; @@ -683,10 +690,377 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR return execute((ModifySshKeysCommand) cmd); } else if (cmd instanceof PoolEjectCommand) { return execute((PoolEjectCommand) cmd); + } else if (cmd instanceof Start2Command) { + return execute((Start2Command)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } + + Pair getNetworkForTraffic(Connection conn, TrafficType type) throws XenAPIException, XmlRpcException { + if (type == TrafficType.Guest) { + return new Pair(Network.getByUuid(conn, _host.guestNetwork), _host.guestPif); + } else if (type == TrafficType.LinkLocal) { + return new Pair(Network.getByUuid(conn, _host.linkLocalNetwork), null); + } else if (type == TrafficType.Management) { + return new Pair(Network.getByUuid(conn, _host.privateNetwork), _host.privatePif); + } else if (type == TrafficType.Public) { + return new Pair(Network.getByUuid(conn, _host.publicNetwork), _host.publicPif); + } else if (type == TrafficType.Storage) { + return new Pair(Network.getByUuid(conn, _host.storageNetwork1), _host.storagePif1); + } else if (type == TrafficType.Vpn) { + return new Pair(Network.getByUuid(conn, _host.publicNetwork), _host.publicPif); + } + + throw new CloudRuntimeException("Unsupported network type: " + type); + } + + protected VIF createVif(Connection conn, String vmName, VM vm, NicTO nic) throws XmlRpcException, XenAPIException { + VIF.Record vifr = new VIF.Record(); + vifr.VM = vm; + vifr.device = Integer.toString(nic.getDeviceId()); + vifr.MAC = nic.getMac(); + + Pair network = getNetworkForTraffic(conn, nic.getType()); + if (nic.getBroadcastType() == BroadcastDomainType.Vlan) { + vifr.network = enableVlanNetwork(conn, nic.getVlan(), network.first(), network.second()); + } else { + vifr.network = network.first(); + } + + if (nic.getNetworkRateMbps() != null) { + vifr.qosAlgorithmType = "ratelimit"; + vifr.qosAlgorithmParams = new HashMap(); + vifr.qosAlgorithmParams.put("kbps", Integer.toString(nic.getNetworkRateMbps() * 1000)); + } + + VIF vif = VIF.create(conn, vifr); + if (s_logger.isDebugEnabled()) { + vifr = vif.getRecord(conn); + s_logger.debug("Created a vif " + vifr.uuid + " on " + nic.getDeviceId()); + } + + return vif; + } + + protected VDI mount(Connection conn, String vmName, VolumeTO volume) throws XmlRpcException, XenAPIException { + if (volume.getType() == VolumeType.ISO) { + String isopath = volume.getPath(); + int index = isopath.lastIndexOf("/"); + + String mountpoint = isopath.substring(0, index); + URI uri; + try { + uri = new URI(mountpoint); + } catch (URISyntaxException e) { + throw new CloudRuntimeException("Incorrect uri " + mountpoint, e); + } + SR isoSr = createIsoSRbyURI(uri, vmName, false); + + String isoname = isopath.substring(index + 1); + + VDI isoVdi = getVDIbyLocationandSR(isoname, isoSr); + + if (isoVdi == null) { + throw new CloudRuntimeException("Unable to find ISO " + volume.getPath()); + } + return isoVdi; + } else { + return VDI.getByUuid(conn, volume.getPath()); + } + } + + protected VBD createVbd(Connection conn, String vmName, VM vm, VolumeTO volume, boolean patch) throws XmlRpcException, XenAPIException { + VolumeType type = volume.getType(); + + VDI vdi = mount(conn, vmName, volume); + + if (patch) { + if (!patchSystemVm(vdi, vmName)) { + throw new CloudRuntimeException("Unable to patch system vm"); + } + } + + VBD.Record vbdr = new VBD.Record(); + vbdr.VM = vm; + vbdr.VDI = vdi; + if (type == VolumeType.ROOT) { + vbdr.bootable = true; + } + vbdr.userdevice = Long.toString(volume.getDeviceId()); + if (volume.getType() == VolumeType.ISO) { + vbdr.mode = Types.VbdMode.RO; + vbdr.type = Types.VbdType.CD; + } else { + vbdr.mode = Types.VbdMode.RW; + vbdr.type = Types.VbdType.DISK; + + } + + VBD vbd = VBD.create(conn, vbdr); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("VBD " + vbd.getUuid(conn) + " created for " + volume); + } + + return vbd; + } + + protected Pair createVmFromTemplate(Connection conn, VirtualMachineTO vmSpec, Host host) throws XenAPIException, XmlRpcException { + String guestOsTypeName = getGuestOsType(vmSpec.getOs()); + Set templates = VM.getByNameLabel(conn, guestOsTypeName); + assert templates.size() == 1 : "Should only have 1 template but found " + templates.size(); + VM template = templates.iterator().next(); + + VM vm = template.createClone(conn, vmSpec.getName()); + vm.setAffinity(conn, host); + + VM.Record vmr = vm.getRecord(conn); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Created VM " + vmr.uuid + " for " + vmSpec.getName()); + } + + for (Console console : vmr.consoles) { + console.destroy(conn); + } + + vm.setIsATemplate(conn, false); + vm.removeFromOtherConfig(conn, "disks"); + vm.setNameLabel(conn, vmSpec.getName()); + setMemory(conn, vm, vmSpec.getMinRam()); + vm.setVCPUsAtStartup(conn, (long)vmSpec.getCpus()); + vm.setVCPUsMax(conn, (long)vmSpec.getCpus()); + vm.setVCPUsNumberLive(conn, (long)vmSpec.getCpus()); + + Map vcpuParams = new HashMap(); + + if (vmSpec.getWeight() != null) { + vcpuParams.put("weight", Integer.toString(vmSpec.getWeight())); + } + if (vmSpec.getUtilization() != null) { + vcpuParams.put("cap", Integer.toString(vmSpec.getUtilization())); + } + if (vcpuParams.size() > 0) { + vm.setVCPUsParams(conn, vcpuParams); + } + + vm.setActionsAfterCrash(conn, Types.OnCrashBehaviour.DESTROY); + vm.setActionsAfterShutdown(conn, Types.OnNormalExit.DESTROY); + + String bootArgs = vmSpec.getBootArgs(); + if (bootArgs != null && bootArgs.length() > 0) { + String pvargs = vm.getPVArgs(conn); + pvargs = pvargs + vmSpec.getBootArgs(); + if (s_logger.isDebugEnabled()) { + s_logger.debug("PV args are " + pvargs); + } + vm.setPVArgs(conn, pvargs); + } + + if (!(guestOsTypeName.startsWith("Windows") || guestOsTypeName.startsWith("Citrix") || guestOsTypeName.startsWith("Other"))) { + if (vmSpec.getBootloader() == BootloaderType.CD) { + vm.setPVBootloader(conn, "eliloader"); + vm.addToOtherConfig(conn, "install-repository", "cdrom"); + } else if (vmSpec.getBootloader() == BootloaderType.PyGrub ){ + vm.setPVBootloader(conn, "pygrub"); + } else { + vm.destroy(conn); + throw new CloudRuntimeException("Unable to handle boot loader type: " + vmSpec.getBootloader()); + } + } + + return new Pair(vm, vmr.uuid); + } + + protected String handleVmStartFailure(String vmName, VM vm, String message, Throwable th) { + String msg = "Unable to start " + vmName + " due to " + message; + s_logger.warn(msg, th); + + if (vm == null) { + return msg; + } + + Connection conn = getConnection(); + try { + VM.Record vmr = vm.getRecord(conn); + if (vmr.powerState == VmPowerState.RUNNING) { + try { + vm.hardShutdown(conn); + } catch (Exception e) { + s_logger.warn("VM hardshutdown failed due to ", e); + } + } + if (vm.getPowerState(conn) == VmPowerState.HALTED) { + try { + vm.destroy(conn); + } catch (Exception e) { + s_logger.warn("VM destroy failed due to ", e); + } + } + for (VBD vbd : vmr.VBDs) { + try { + vbd.unplug(conn); + vbd.destroy(conn); + } catch (Exception e) { + s_logger.warn("Unable to clean up VBD due to ", e); + } + } + for (VIF vif : vmr.VIFs) { + try { + vif.unplug(conn); + vif.destroy(conn); + } catch (Exception e) { + s_logger.warn("Unable to cleanup VIF", e); + } + } + } catch (Exception e) { + s_logger.warn("VM getRecord failed due to ", e); + } + + return msg; + } + + protected Start2Answer execute(Start2Command cmd) { + VirtualMachineTO vmSpec = cmd.getVirtualMachine(); + String vmName = vmSpec.getName(); + + Connection conn = getConnection(); + State state = State.Stopped; + VM vm = null; + try { + Host host = Host.getByUuid(conn, _host.uuid); + synchronized (_vms) { + _vms.put(vmName, State.Starting); + } + + Pair v = createVmFromTemplate(conn, vmSpec, host); + vm = v.first(); + String vmUuid = v.second(); + + for (VolumeTO disk : vmSpec.getDisks()) { + createVbd(conn, vmName, vm, disk, disk.getType() == VolumeType.ROOT && vmSpec.getType() != VirtualMachine.Type.User); + } + + NicTO controlNic = null; + for (NicTO nic : vmSpec.getNetworks()) { + if (nic.getControlPort() != null) { + controlNic = nic; + } + createVif(conn, vmName, vm, nic); + } + + /* + * + VBD.Record vbdr = new VBD.Record(); + Ternary mount = mounts.get(0); + vbdr.VM = vm; + vbdr.VDI = mount.second(); + vbdr.bootable = !bootFromISO; + vbdr.userdevice = "0"; + vbdr.mode = Types.VbdMode.RW; + vbdr.type = Types.VbdType.DISK; + VBD.create(conn, vbdr); + + for (int i = 1; i < mounts.size(); i++) { + mount = mounts.get(i); + // vdi.setNameLabel(conn, cmd.getVmName() + "-DATA"); + vbdr.VM = vm; + vbdr.VDI = mount.second(); + vbdr.bootable = false; + vbdr.userdevice = Long.toString(mount.third().getDeviceId()); + vbdr.mode = Types.VbdMode.RW; + vbdr.type = Types.VbdType.DISK; + vbdr.unpluggable = true; + VBD.create(conn, vbdr); + + } + + VBD.Record cdromVBDR = new VBD.Record(); + cdromVBDR.VM = vm; + cdromVBDR.empty = true; + cdromVBDR.bootable = bootFromISO; + cdromVBDR.userdevice = "3"; + cdromVBDR.mode = Types.VbdMode.RO; + cdromVBDR.type = Types.VbdType.CD; + VBD cdromVBD = VBD.create(conn, cdromVBDR); + + String isopath = cmd.getISOPath(); + if (isopath != null) { + int index = isopath.lastIndexOf("/"); + + String mountpoint = isopath.substring(0, index); + URI uri = new URI(mountpoint); + isosr = createIsoSRbyURI(uri, cmd.getVmName(), false); + + String isoname = isopath.substring(index + 1); + + VDI isovdi = getVDIbyLocationandSR(isoname, isosr); + + if (isovdi == null) { + String msg = " can not find ISO " + cmd.getISOPath(); + s_logger.warn(msg); + return new StartAnswer(cmd, msg); + } else { + cdromVBD.insert(conn, isovdi); + } + + } + */ + + vm.startOn(conn, host, false, true); + + if (_canBridgeFirewall) { + String result = null; + if (vmSpec.getType() != VirtualMachine.Type.User) { + result = callHostPlugin("vmops", "default_network_rules_systemvm", "vmName", vmName); + } else { + } + + if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { + s_logger.warn("Failed to program default network rules for " + vmName); + } else { + s_logger.info("Programmed default network rules for " + vmName); + } + } + + if (controlNic != null) { + String privateIp = controlNic.getIp(); + int cmdPort = controlNic.getControlPort(); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Ping command port, " + privateIp + ":" + cmdPort); + } + + String result = connect(vmName, privateIp, cmdPort); + if (result != null) { + throw new CloudRuntimeException("Can not ping System vm " + vmName + "due to:" + result); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Ping command port succeeded for vm " + vmName); + } + } + + state = State.Running; + return new Start2Answer(cmd); + } catch (XmlRpcException e) { + String msg = handleVmStartFailure(vmName, vm, "", e); + return new Start2Answer(cmd, msg); + } catch (XenAPIException e) { + String msg = handleVmStartFailure(vmName, vm, "", e); + return new Start2Answer(cmd, msg); + } catch (Exception e) { + String msg = handleVmStartFailure(vmName, vm, "", e); + return new Start2Answer(cmd, msg); + } finally { + synchronized (_vms) { + if (state != State.Stopped) { + _vms.put(vmName, state); + } else { + _vms.remove(vmName); + } + } + } + } protected Answer execute(ModifySshKeysCommand cmd) { String publickey = cmd.getPubKey(); @@ -1695,6 +2069,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR } } + @Override public DownloadAnswer execute(final PrimaryStorageDownloadCommand cmd) { SR tmpltsr = null; String tmplturl = cmd.getUrl(); @@ -3292,6 +3667,45 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR return null; } + protected synchronized Network getNetworkByName(Connection conn, String name, boolean lookForPif) throws XenAPIException, XmlRpcException { + Network found = null; + Set networks = Network.getByNameLabel(conn, name); + if (networks.size() == 1) { + found = networks.iterator().next(); + } else if (networks.size() > 1) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found more than one network with the name " + name); + } + for (Network network : networks) { + if (!lookForPif) { + found = network; + break; + } + + Network.Record netr = network.getRecord(conn); + s_logger.debug("Checking network " + netr.uuid); + if (netr.PIFs.size() == 0) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Network " + netr.uuid + " has no pifs so skipping that."); + } + } else { + for (PIF pif : netr.PIFs) { + PIF.Record pifr = pif.getRecord(conn); + if (_host.uuid.equals(pifr.host.getUuid(conn))) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Network " + netr.uuid + " has a pif " + pifr.uuid + " for our host "); + } + found = network; + break; + } + } + } + } + } + + return found; + } + protected Network enableVlanNetwork(long tag, String networkUuid, String pifUuid) throws XenAPIException, XmlRpcException { // In XenServer, vlan is added by // 1. creating a network. @@ -3344,6 +3758,60 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR return vlanNetwork; } + protected Network enableVlanNetwork(Connection conn, long tag, Network network, String pifUuid) throws XenAPIException, XmlRpcException { + // In XenServer, vlan is added by + // 1. creating a network. + // 2. creating a vlan associating network with the pif. + // We always create + // 1. a network with VLAN[vlan id in decimal] + // 2. a vlan associating the network created with the pif to private + // network. + + Network vlanNetwork = null; + String name = "VLAN" + Long.toString(tag); + + vlanNetwork = getNetworkByName(conn, name, true); + if (vlanNetwork == null) { // Can't find it, then create it. + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating VLAN network for " + tag + " on host " + _host.ip); + } + Network.Record nwr = new Network.Record(); + nwr.nameLabel = name; + nwr.bridge = name; + vlanNetwork = Network.create(conn, nwr); + } + + PIF nPif = PIF.getByUuid(conn, pifUuid); + PIF.Record nPifr = nPif.getRecord(conn); + + Network.Record vlanNetworkr = vlanNetwork.getRecord(conn); + if (vlanNetworkr.PIFs != null) { + for (PIF pif : vlanNetworkr.PIFs) { + PIF.Record pifr = pif.getRecord(conn); + if (pifr.device.equals(nPifr.device) && pifr.host.equals(nPifr.host)) { + pif.plug(conn); + return vlanNetwork; + } + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating VLAN " + tag + " on host " + _host.ip + " on device " + nPifr.device); + } + VLAN vlan = VLAN.create(conn, nPif, tag, vlanNetwork); + VLAN.Record vlanr = vlan.getRecord(conn); + if (s_logger.isDebugEnabled()) { + s_logger.debug("VLAN is created for " + tag + ". The uuid is " + vlanr.uuid); + } + + PIF untaggedPif = vlanr.untaggedPIF; + if (!untaggedPif.getCurrentlyAttached(conn)) { + untaggedPif.plug(conn); + } + + return vlanNetwork; + } + protected void disableVlanNetwork(Network network) throws InternalErrorException { try { Connection conn = getConnection(); @@ -4263,6 +4731,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR } } + @Override public CreateAnswer execute(CreateCommand cmd) { StoragePoolTO pool = cmd.getPool(); DiskCharacteristics dskch = cmd.getDiskCharacteristics(); @@ -4762,6 +5231,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR } } + @Override public Answer execute(DestroyCommand cmd) { VolumeTO vol = cmd.getVolume(); @@ -4805,6 +5275,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR return new Answer(cmd, true, "Success"); } + @Override public ShareAnswer execute(final ShareCommand cmd) { if (!cmd.isShare()) { SR sr = getISOSRbyVmName(cmd.getVmName()); @@ -4824,6 +5295,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR return new ShareAnswer(cmd, new HashMap()); } + @Override public CopyVolumeAnswer execute(final CopyVolumeCommand cmd) { String volumeUUID = cmd.getVolumePath(); StoragePoolVO pool = cmd.getPool(); @@ -6076,10 +6548,12 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR _agentControl = agentControl; } + @Override public boolean IsRemoteAgent() { return _isRemoteAgent; } + @Override public void setRemoteAgent(boolean remote) { _isRemoteAgent = remote; } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 5f87093cb8a..21ee3bcd874 100644 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -77,6 +77,14 @@ DROP TABLE IF EXISTS `cloud`.`nics`; DROP TABLE IF EXISTS `cloud`.`network_profiles`; DROP TABLE IF EXISTS `cloud`.`network_offerings`; DROP TABLE IF EXISTS `cloud`.`host_master`; +DROP TABLE IF EXISTS `cloud`.`hypervisor_properties`; + +CREATE TABLE `cloud`.`hypervsior_properties` ( + `hypervisor` varchar(32) NOT NULL UNIQUE COMMENT 'hypervisor type', + `max_storage_devices` int(10) NOT NULL COMMENT 'maximum number of storage devices', + `cdrom_device` int(10) NOT NULL COMMENT 'device id reserved for cdrom', + `max_network_devices` int(10) NOT NULL COMMENT 'maximum number of network devices', +) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`network_profiles` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',