CLOUDSTACK-9813: Extending Config Drive support (#2097)

Extending Config Drive support

* Added support for VMware
* Build configdrive.iso on ssvm
* Added support for VPC and Isolated Networks
* Moved implementation to new Service Provider
* UI fix: add support for urlencoded userdata
* Add support for building systemvm behind a proxy

Co-Authored-By: Raf Smeets <raf.smeets@nuagenetworks.net>
Co-Authored-By: Frank Maximus <frank.maximus@nuagenetworks.net>
Co-Authored-By: Sigert Goeminne <sigert.goeminne@nuagenetworks.net>
This commit is contained in:
Frank Maximus 2018-01-12 10:44:40 +01:00 committed by Rohit Yadav
parent 7ca4582a85
commit b176648f90
46 changed files with 6118 additions and 316 deletions

View File

@ -142,6 +142,8 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
public static final Provider GloboDns = new Provider("GloboDns", true); public static final Provider GloboDns = new Provider("GloboDns", true);
// add Big Switch Bcf Provider // add Big Switch Bcf Provider
public static final Provider BigSwitchBcf = new Provider("BigSwitchBcf", false); public static final Provider BigSwitchBcf = new Provider("BigSwitchBcf", false);
//Add ConfigDrive provider
public static final Provider ConfigDrive = new Provider("ConfigDrive", false);
private final String name; private final String name;
private final boolean isExternal; private final boolean isExternal;

View File

@ -17,6 +17,8 @@
package com.cloud.network; package com.cloud.network;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -48,6 +50,28 @@ import org.apache.cloudstack.framework.config.ConfigKey;
* participants in the orchestration can use this interface to query the data. * participants in the orchestration can use this interface to query the data.
*/ */
public interface NetworkModel { public interface NetworkModel {
String METATDATA_DIR = "metadata";
String USERDATA_DIR = "userdata";
String USERDATA_FILE = "user_data";
String PASSWORD_DIR = "password";
String PASSWORD_FILE = "vm_password";
String PASSWORD_CHECKSUM_FILE = "vm-password-md5checksum";
String SERVICE_OFFERING_FILE = "service-offering";
String AVAILABILITY_ZONE_FILE = "availability-zone";
String LOCAL_HOSTNAME_FILE = "local-hostname";
String INSTANCE_ID_FILE = "instance-id";
String VM_ID_FILE = "vm-id";
String PUBLIC_KEYS_FILE = "public-keys";
String CLOUD_IDENTIFIER_FILE = "cloud-identifier";
int CONFIGDATA_DIR = 0;
int CONFIGDATA_FILE = 1;
int CONFIGDATA_CONTENT = 2;
ImmutableMap<String, String> openStackFileMapping = ImmutableMap.of(
AVAILABILITY_ZONE_FILE, "availability_zone",
LOCAL_HOSTNAME_FILE, "hostname",
VM_ID_FILE, "uuid",
INSTANCE_ID_FILE, "name"
);
static final ConfigKey<Integer> MACIdentifier = new ConfigKey<Integer>("Advanced",Integer.class, "mac.identifier", "0", static final ConfigKey<Integer> MACIdentifier = new ConfigKey<Integer>("Advanced",Integer.class, "mac.identifier", "0",
"This value will be used while generating the mac addresses for isolated and shared networks. The hexadecimal equivalent value will be present at the 2nd octet of the mac address. Default value is null which means this feature is disabled.Its scope is global.", true, ConfigKey.Scope.Global); "This value will be used while generating the mac addresses for isolated and shared networks. The hexadecimal equivalent value will be present at the 2nd octet of the mac address. Default value is null which means this feature is disabled.Its scope is global.", true, ConfigKey.Scope.Global);

View File

@ -24,6 +24,8 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.ACL;
@ -47,7 +49,6 @@ import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
import com.cloud.event.EventTypes; import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ConcurrentOperationException;

View File

@ -21,6 +21,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.ACL;
@ -35,7 +37,6 @@ import org.apache.cloudstack.api.response.GuestOSResponse;
import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse;
import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.ResourceUnavailableException;

View File

@ -0,0 +1,55 @@
// 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;
public class AttachIsoAnswer extends Answer {
private Integer deviceKey;
public AttachIsoAnswer(AttachIsoCommand cmd, String result) {
super(cmd, false, result);
this.deviceKey = null;
}
public AttachIsoAnswer(AttachIsoCommand cmd, Integer deviceId) {
super(cmd);
this.deviceKey = deviceId;
}
public AttachIsoAnswer(AttachIsoCommand cmd) {
super(cmd);
this.deviceKey = null;
}
public AttachIsoAnswer(AttachIsoCommand command, boolean success, String details) {
super(command,success,details);
this.deviceKey = null;
}
public AttachIsoAnswer(Command command, Exception e) {
super(command, e);
}
public Integer getDeviceKey() {
return deviceKey;
}
public void setDeviceKey(Integer deviceKey) {
this.deviceKey = deviceKey;
}
}

View File

@ -25,14 +25,22 @@ public class AttachIsoCommand extends Command {
private String storeUrl; private String storeUrl;
private String isoPath; private String isoPath;
private boolean attach; private boolean attach;
private Integer deviceKey;
private boolean force;
protected AttachIsoCommand() { protected AttachIsoCommand() {
} }
public AttachIsoCommand(String vmName, String isoPath, boolean attach) { public AttachIsoCommand(String vmName, String isoPath, boolean attach, Integer deviceKey, boolean force) {
this.vmName = vmName; this.vmName = vmName;
this.isoPath = isoPath; this.isoPath = isoPath;
this.attach = attach; this.attach = attach;
this.deviceKey = deviceKey;
this.force = force;
}
public AttachIsoCommand(String vmName, String isoPath, boolean attach) {
this(vmName, isoPath, attach, null, false);
} }
@Override @Override
@ -52,6 +60,10 @@ public class AttachIsoCommand extends Command {
return attach; return attach;
} }
public void setAttach(boolean attach) {
this.attach = attach;
}
public String getStoreUrl() { public String getStoreUrl() {
return storeUrl; return storeUrl;
} }
@ -59,4 +71,16 @@ public class AttachIsoCommand extends Command {
public void setStoreUrl(String url) { public void setStoreUrl(String url) {
storeUrl = url; storeUrl = url;
} }
public void setDeviceKey(Integer deviceKey) {
this.deviceKey = deviceKey;
}
public Integer getDeviceKey() {
return deviceKey;
}
public boolean isForce() {
return force;
}
} }

View File

@ -0,0 +1,78 @@
//
// 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;
import java.util.List;
import com.cloud.agent.api.to.DataStoreTO;
public class HandleConfigDriveIsoCommand extends Command {
String isoFile;
List<String[]> vmData;
String configDriveLabel;
boolean create = false;
private boolean update = false;
private DataStoreTO destStore;
public HandleConfigDriveIsoCommand(List<String[]> vmData, String label, DataStoreTO destStore, String isoFile, boolean create, boolean update) {
this.vmData = vmData;
this.configDriveLabel = label;
this.create = create;
this.update = update;
this.destStore = destStore;
this.isoFile = isoFile;
}
@Override
public boolean executeInSequence() {
return false;
}
public List<String[]> getVmData() {
return vmData;
}
public void setVmData(List<String[]> vmData) {
this.vmData = vmData;
}
public boolean isCreate() {
return create;
}
public String getConfigDriveLabel() {
return configDriveLabel;
}
public DataStoreTO getDestStore() {
return destStore;
}
public String getIsoFile() {
return isoFile;
}
public boolean isUpdate() {
return update;
}
}

View File

@ -19,16 +19,24 @@
package org.apache.cloudstack.api.agent.test; package org.apache.cloudstack.api.agent.test;
import org.junit.Assert;
import org.junit.Test;
import com.google.gson.Gson;
import com.cloud.agent.api.AttachIsoCommand;
import com.cloud.agent.api.Command;
import com.cloud.serializer.GsonHelper;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.Test;
import com.cloud.agent.api.AttachIsoCommand;
public class AttachIsoCommandTest { public class AttachIsoCommandTest {
AttachIsoCommand aic = new AttachIsoCommand("vmname", "isopath", false);
private static final Gson s_gson = GsonHelper.getGson();
AttachIsoCommand aic = new AttachIsoCommand("vmname", "isopath", false, 1, true);
@Test @Test
public void testGetVmName() { public void testGetVmName() {
@ -80,4 +88,17 @@ public class AttachIsoCommandTest {
b = aic.getWait(); b = aic.getWait();
assertEquals(b, 0); assertEquals(b, 0);
} }
@Test
public void testSerialization() {
AttachIsoCommand after = serializeAndDeserialize(aic);
Assert.assertEquals(aic, after);
}
private <T extends Command> T serializeAndDeserialize(T command) {
final String json = s_gson.toJson(new Command[] {command});
Command[] forwardedCommands = s_gson.fromJson(json, Command[].class);
return (T) forwardedCommands[0];
}
} }

View File

@ -52,7 +52,7 @@ public interface VirtualMachineManager extends Manager {
static final ConfigKey<Boolean> ExecuteInSequence = new ConfigKey<Boolean>("Advanced", Boolean.class, "execute.in.sequence.hypervisor.commands", "false", static final ConfigKey<Boolean> ExecuteInSequence = new ConfigKey<Boolean>("Advanced", Boolean.class, "execute.in.sequence.hypervisor.commands", "false",
"If set to true, start, stop, reboot, copy and migrate commands will be serialized on the agent side. If set to false the commands are executed in parallel. Default value is false.", false); "If set to true, start, stop, reboot, copy and migrate commands will be serialized on the agent side. If set to false the commands are executed in parallel. Default value is false.", false);
static final ConfigKey<String> VmConfigDriveLabel = new ConfigKey<String>("Hidden", String.class, "vm.configdrive.label", "config", static final ConfigKey<String> VmConfigDriveLabel = new ConfigKey<String>("Hidden", String.class, "vm.configdrive.label", "config-2",
"The default label name for the config drive", false); "The default label name for the config drive", false);
public interface Topics { public interface Topics {

View File

@ -41,7 +41,6 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
@ -69,7 +68,6 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
@ -2251,16 +2249,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
DiskDef.DiskBus diskBusTypeData = (diskBusType == DiskDef.DiskBus.SCSI) ? diskBusType : DiskDef.DiskBus.VIRTIO; DiskDef.DiskBus diskBusTypeData = (diskBusType == DiskDef.DiskBus.SCSI) ? diskBusType : DiskDef.DiskBus.VIRTIO;
final DiskDef disk = new DiskDef(); final DiskDef disk = new DiskDef();
int devId = volume.getDiskSeq().intValue();
if (volume.getType() == Volume.Type.ISO) { if (volume.getType() == Volume.Type.ISO) {
if (volPath == null) { if (volPath == null) {
/* Add iso as placeholder */ /* Add iso as placeholder */
disk.defISODisk(null); disk.defISODisk(null, devId);
} else { } else {
disk.defISODisk(volPath); disk.defISODisk(volPath, devId);
} }
} else { } else {
final int devId = volume.getDiskSeq().intValue();
if (diskBusType == DiskDef.DiskBus.SCSI ) { if (diskBusType == DiskDef.DiskBus.SCSI ) {
disk.setQemuDriver(true); disk.setQemuDriver(true);
disk.setDiscard(DiscardType.UNMAP); disk.setDiscard(DiscardType.UNMAP);
@ -2390,9 +2387,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return _storagePoolMgr; return _storagePoolMgr;
} }
public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach) throws LibvirtException, URISyntaxException, public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, final Integer diskSeq) throws LibvirtException, URISyntaxException,
InternalErrorException { InternalErrorException {
String isoXml = null; final DiskDef iso = new DiskDef();
if (isoPath != null && isAttach) { if (isoPath != null && isAttach) {
final int index = isoPath.lastIndexOf("/"); final int index = isoPath.lastIndexOf("/");
final String path = isoPath.substring(0, index); final String path = isoPath.substring(0, index);
@ -2401,20 +2398,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
isoPath = isoVol.getPath(); isoPath = isoVol.getPath();
final DiskDef iso = new DiskDef(); iso.defISODisk(isoPath, diskSeq);
iso.defISODisk(isoPath);
isoXml = iso.toString();
} else { } else {
final DiskDef iso = new DiskDef(); iso.defISODisk(null, diskSeq);
iso.defISODisk(null);
isoXml = iso.toString();
} }
final List<DiskDef> disks = getDisks(conn, vmName); final String result = attachOrDetachDevice(conn, true, vmName, iso.toString());
final String result = attachOrDetachDevice(conn, true, vmName, isoXml);
if (result == null && !isAttach) { if (result == null && !isAttach) {
final List<DiskDef> disks = getDisks(conn, vmName);
for (final DiskDef disk : disks) { for (final DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM) { if (disk.getDeviceType() == DiskDef.DeviceType.CDROM
&& (diskSeq == null || disk.getDiskLabel() == iso.getDiskLabel())) {
cleanupDisk(disk); cleanupDisk(disk);
} }
} }

View File

@ -22,7 +22,6 @@ import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@ -116,7 +115,7 @@ public class LibvirtDomainXMLParser {
} }
def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt); def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
} else if (device.equalsIgnoreCase("cdrom")) { } else if (device.equalsIgnoreCase("cdrom")) {
def.defISODisk(diskFile); def.defISODisk(diskFile , i+1);
} }
} else if (type.equalsIgnoreCase("block")) { } else if (type.equalsIgnoreCase("block")) {
def.defBlockBasedDisk(diskDev, diskLabel, def.defBlockBasedDisk(diskDev, diskLabel,

View File

@ -609,22 +609,24 @@ public class LibvirtVMDef {
} }
/* skip iso label */ /* skip iso labels */
private String getDevLabel(int devId, DiskBus bus) { private String getDevLabel(int devId, DiskBus bus, boolean forIso) {
if (devId < 0) { if (devId < 0) {
return ""; return "";
} }
if (devId == 2) {
devId++;
}
if (bus == DiskBus.SCSI) { if (bus == DiskBus.SCSI) {
return "sd" + getDevLabelSuffix(devId); return "sd" + getDevLabelSuffix(devId);
} else if (bus == DiskBus.VIRTIO) { } else if (bus == DiskBus.VIRTIO) {
return "vd" + getDevLabelSuffix(devId); return "vd" + getDevLabelSuffix(devId);
} }
if (forIso) {
devId --;
} else if(devId >= 2) {
devId += 2;
}
return "hd" + getDevLabelSuffix(devId); return "hd" + getDevLabelSuffix(devId);
} }
private String getDevLabelSuffix(int deviceIndex) { private String getDevLabelSuffix(int deviceIndex) {
@ -649,7 +651,7 @@ public class LibvirtVMDef {
_deviceType = DeviceType.DISK; _deviceType = DeviceType.DISK;
_diskCacheMode = DiskCacheMode.NONE; _diskCacheMode = DiskCacheMode.NONE;
_sourcePath = filePath; _sourcePath = filePath;
_diskLabel = getDevLabel(devId, bus); _diskLabel = getDevLabel(devId, bus, false);
_diskFmtType = diskFmtType; _diskFmtType = diskFmtType;
_bus = bus; _bus = bus;
@ -659,19 +661,33 @@ public class LibvirtVMDef {
_diskType = DiskType.FILE; _diskType = DiskType.FILE;
_deviceType = DeviceType.CDROM; _deviceType = DeviceType.CDROM;
_sourcePath = volPath; _sourcePath = volPath;
_diskLabel = "hdc"; _diskLabel = getDevLabel(3, DiskBus.IDE, true);
_diskFmtType = DiskFmtType.RAW; _diskFmtType = DiskFmtType.RAW;
_diskCacheMode = DiskCacheMode.NONE; _diskCacheMode = DiskCacheMode.NONE;
_bus = DiskBus.IDE; _bus = DiskBus.IDE;
} }
public void defISODisk(String volPath, Integer devId) {
if (devId == null) {
defISODisk(volPath);
} else {
_diskType = DiskType.FILE;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
_diskLabel = getDevLabel(devId, DiskBus.IDE, true);
_diskFmtType = DiskFmtType.RAW;
_diskCacheMode = DiskCacheMode.NONE;
_bus = DiskBus.IDE;
}
}
public void defBlockBasedDisk(String diskName, int devId, DiskBus bus) { public void defBlockBasedDisk(String diskName, int devId, DiskBus bus) {
_diskType = DiskType.BLOCK; _diskType = DiskType.BLOCK;
_deviceType = DeviceType.DISK; _deviceType = DeviceType.DISK;
_diskFmtType = DiskFmtType.RAW; _diskFmtType = DiskFmtType.RAW;
_diskCacheMode = DiskCacheMode.NONE; _diskCacheMode = DiskCacheMode.NONE;
_sourcePath = diskName; _sourcePath = diskName;
_diskLabel = getDevLabel(devId, bus); _diskLabel = getDevLabel(devId, bus, false);
_bus = bus; _bus = bus;
} }
@ -696,7 +712,7 @@ public class LibvirtVMDef {
_sourcePort = sourcePort; _sourcePort = sourcePort;
_authUserName = authUserName; _authUserName = authUserName;
_authSecretUUID = authSecretUUID; _authSecretUUID = authSecretUUID;
_diskLabel = getDevLabel(devId, bus); _diskLabel = getDevLabel(devId, bus, false);
_bus = bus; _bus = bus;
_diskProtocol = protocol; _diskProtocol = protocol;
} }

View File

@ -24,7 +24,7 @@ import java.net.URISyntaxException;
import org.libvirt.Connect; import org.libvirt.Connect;
import org.libvirt.LibvirtException; import org.libvirt.LibvirtException;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoAnswer;
import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachIsoCommand;
import com.cloud.exception.InternalErrorException; import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
@ -32,23 +32,19 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper; import com.cloud.resource.ResourceWrapper;
@ResourceWrapper(handles = AttachIsoCommand.class) @ResourceWrapper(handles = AttachIsoCommand.class)
public final class LibvirtAttachIsoCommandWrapper extends CommandWrapper<AttachIsoCommand, Answer, LibvirtComputingResource> { public final class LibvirtAttachIsoCommandWrapper extends CommandWrapper<AttachIsoCommand, AttachIsoAnswer, LibvirtComputingResource> {
@Override @Override
public Answer execute(final AttachIsoCommand command, final LibvirtComputingResource libvirtComputingResource) { public AttachIsoAnswer execute(final AttachIsoCommand command, final LibvirtComputingResource libvirtComputingResource) {
try { try {
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(command.getVmName()); final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(command.getVmName());
libvirtComputingResource.attachOrDetachISO(conn, command.getVmName(), command.getIsoPath(), command.isAttach()); libvirtComputingResource.attachOrDetachISO(conn, command.getVmName(), command.getIsoPath(), command.isAttach(), command.getDeviceKey());
} catch (final LibvirtException e) { } catch (final LibvirtException|URISyntaxException|InternalErrorException e) {
return new Answer(command, false, e.toString()); return new AttachIsoAnswer(command, e);
} catch (final URISyntaxException e) {
return new Answer(command, false, e.toString());
} catch (final InternalErrorException e) {
return new Answer(command, false, e.toString());
} }
return new Answer(command); return new AttachIsoAnswer(command, command.getDeviceKey());
} }
} }

View File

@ -27,7 +27,6 @@ import java.nio.channels.SocketChannel;
import java.rmi.RemoteException; import java.rmi.RemoteException;
import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.Resource.ResourceType;
import org.joda.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -43,13 +42,13 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.apache.log4j.NDC; import org.apache.log4j.NDC;
import org.apache.commons.lang.StringUtils; import org.joda.time.Duration;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.vmware.vim25.AboutInfo; import com.vmware.vim25.AboutInfo;
import com.vmware.vim25.BoolPolicy; import com.vmware.vim25.BoolPolicy;
@ -85,7 +84,6 @@ import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceBackingInfo; import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDeviceConfigSpec; import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation; import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualUSBController;
import com.vmware.vim25.VirtualDisk; import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo; import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualEthernetCard; import com.vmware.vim25.VirtualEthernetCard;
@ -102,6 +100,7 @@ import com.vmware.vim25.VirtualMachineRelocateSpec;
import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator; import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
import com.vmware.vim25.VirtualMachineRuntimeInfo; import com.vmware.vim25.VirtualMachineRuntimeInfo;
import com.vmware.vim25.VirtualMachineVideoCard; import com.vmware.vim25.VirtualMachineVideoCard;
import com.vmware.vim25.VirtualUSBController;
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
@ -111,10 +110,10 @@ import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.commons.lang.math.NumberUtils; import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import com.cloud.agent.IAgentControl; import com.cloud.agent.IAgentControl;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.AttachIsoAnswer;
import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachIsoCommand;
import com.cloud.agent.api.BackupSnapshotAnswer; import com.cloud.agent.api.BackupSnapshotAnswer;
import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.BackupSnapshotCommand;
@ -259,7 +258,6 @@ import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.NetworkDetails; import com.cloud.hypervisor.vmware.mo.NetworkDetails;
import com.cloud.hypervisor.vmware.mo.TaskMO; import com.cloud.hypervisor.vmware.mo.TaskMO;
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder; import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VirtualSwitchType; import com.cloud.hypervisor.vmware.mo.VirtualSwitchType;
@ -282,8 +280,8 @@ import com.cloud.storage.resource.StoragePoolResource;
import com.cloud.storage.resource.StorageSubsystemCommandHandler; import com.cloud.storage.resource.StorageSubsystemCommandHandler;
import com.cloud.storage.resource.VmwareStorageLayoutHelper; import com.cloud.storage.resource.VmwareStorageLayoutHelper;
import com.cloud.storage.resource.VmwareStorageProcessor; import com.cloud.storage.resource.VmwareStorageProcessor;
import com.cloud.storage.resource.VmwareStorageSubsystemCommandHandler;
import com.cloud.storage.resource.VmwareStorageProcessor.VmwareStorageProcessorConfigurableFields; import com.cloud.storage.resource.VmwareStorageProcessor.VmwareStorageProcessorConfigurableFields;
import com.cloud.storage.resource.VmwareStorageSubsystemCommandHandler;
import com.cloud.storage.template.TemplateProp; import com.cloud.storage.template.TemplateProp;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.ExecutionResult; import com.cloud.utils.ExecutionResult;
@ -1493,7 +1491,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} }
} }
} }
Collections.sort(validatedDisks, (d1, d2) -> d1.getDiskSeq().compareTo(d2.getDiskSeq()));
return validatedDisks.toArray(new DiskTO[0]); return validatedDisks.toArray(new DiskTO[0]);
} }
@ -1880,10 +1878,14 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first()));
deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT);
} }
i++;
} else { } else {
// Note: we will always plug a CDROM device // Note: we will always plug a CDROM device
if (volIso != null) { if (volIso != null) {
TemplateObjectTO iso = (TemplateObjectTO)volIso.getData(); for (DiskTO vol : disks) {
if (vol.getType() == Volume.Type.ISO) {
TemplateObjectTO iso = (TemplateObjectTO) vol.getData();
if (iso.getPath() != null && !iso.getPath().isEmpty()) { if (iso.getPath() != null && !iso.getPath().isEmpty()) {
DataStoreTO imageStore = iso.getDataStore(); DataStoreTO imageStore = iso.getDataStore();
@ -1891,15 +1893,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
s_logger.debug("unsupported protocol"); s_logger.debug("unsupported protocol");
throw new Exception("unsupported protocol"); throw new Exception("unsupported protocol");
} }
NfsTO nfsImageStore = (NfsTO)imageStore; NfsTO nfsImageStore = (NfsTO) imageStore;
String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath();
Pair<String, ManagedObjectReference> isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath); Pair<String, ManagedObjectReference> isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath);
assert (isoDatastoreInfo != null); assert (isoDatastoreInfo != null);
assert (isoDatastoreInfo.second() != null); assert (isoDatastoreInfo.second() != null);
deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec();
Pair<VirtualDevice, Boolean> isoInfo = VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber++, Pair<VirtualDevice, Boolean> isoInfo =
i + 1); VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber++, i + 1);
deviceConfigSpecArray[i].setDevice(isoInfo.first()); deviceConfigSpecArray[i].setDevice(isoInfo.first());
if (isoInfo.second()) { if (isoInfo.second()) {
if (s_logger.isDebugEnabled()) if (s_logger.isDebugEnabled())
@ -1911,6 +1913,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT);
} }
} }
i++;
}
}
} else { } else {
deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec();
Pair<VirtualDevice, Boolean> isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, null, true, true, ideUnitNumber++, i + 1); Pair<VirtualDevice, Boolean> isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, null, true, true, ideUnitNumber++, i + 1);
@ -1926,10 +1931,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT);
} }
i++;
} }
} }
i++;
// //
// Setup ROOT/DATA disk devices // Setup ROOT/DATA disk devices
@ -2910,10 +2916,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
private static VolumeObjectTO getVolumeInSpec(VirtualMachineTO vmSpec, VolumeObjectTO srcVol) { private static VolumeObjectTO getVolumeInSpec(VirtualMachineTO vmSpec, VolumeObjectTO srcVol) {
for (DiskTO disk : vmSpec.getDisks()) { for (DiskTO disk : vmSpec.getDisks()) {
VolumeObjectTO vol = (VolumeObjectTO)disk.getData(); if (disk.getData() instanceof VolumeObjectTO) {
VolumeObjectTO vol = (VolumeObjectTO) disk.getData();
if (vol.getId() == srcVol.getId()) if (vol.getId() == srcVol.getId())
return vol; return vol;
} }
}
return null; return null;
} }
@ -3237,7 +3245,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
String isoFileName = isoUrl.substring(isoFileNameStartPos); String isoFileName = isoUrl.substring(isoFileNameStartPos);
int templateRootPos = isoUrl.indexOf("template/tmpl"); int templateRootPos = isoUrl.indexOf("template/tmpl");
if (templateRootPos < 0) { templateRootPos = (templateRootPos < 0 ? isoUrl.indexOf("ConfigDrive") : templateRootPos);
if (templateRootPos < 0 ) {
throw new Exception("Invalid ISO path info"); throw new Exception("Invalid ISO path info");
} }
@ -4195,7 +4204,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
return str.replace('/', '-'); return str.replace('/', '-');
} }
protected Answer execute(AttachIsoCommand cmd) { protected AttachIsoAnswer execute(AttachIsoCommand cmd) {
if (s_logger.isInfoEnabled()) { if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource AttachIsoCommand: " + _gson.toJson(cmd)); s_logger.info("Executing resource AttachIsoCommand: " + _gson.toJson(cmd));
} }
@ -4221,7 +4230,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} else { } else {
try { try {
if (!vmMo.unmountToolsInstaller()) { if (!vmMo.unmountToolsInstaller()) {
return new Answer(cmd, false, return new AttachIsoAnswer(cmd, false,
"Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); "Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try.");
} }
} catch (Throwable e) { } catch (Throwable e) {
@ -4229,7 +4238,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} }
} }
return new Answer(cmd); return new AttachIsoAnswer(cmd);
} }
} }
@ -4244,7 +4253,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
int isoNameStartPos = isoPath.lastIndexOf('/'); int isoNameStartPos = isoPath.lastIndexOf('/');
String isoFileName = isoPath.substring(isoNameStartPos + 1); String isoFileName = isoPath.substring(isoNameStartPos + 1);
String isoStorePathFromRoot = isoPath.substring(storeUrl.length(), isoNameStartPos); String isoStorePathFromRoot = isoPath.substring(storeUrl.length() + 1, isoNameStartPos + 1);
// TODO, check if iso is already attached, or if there is a previous // TODO, check if iso is already attached, or if there is a previous
// attachment // attachment
@ -4253,12 +4263,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
String isoDatastorePath = String.format("[%s] %s%s", storeName, isoStorePathFromRoot, isoFileName); String isoDatastorePath = String.format("[%s] %s%s", storeName, isoStorePathFromRoot, isoFileName);
if (cmd.isAttach()) { if (cmd.isAttach()) {
vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false); vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, cmd.getDeviceKey());
return new AttachIsoAnswer(cmd);
} else { } else {
vmMo.detachIso(isoDatastorePath); int key = vmMo.detachIso(isoDatastorePath, cmd.isForce());
return new AttachIsoAnswer(cmd, key);
} }
return new Answer(cmd);
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof RemoteException) { if (e instanceof RemoteException) {
s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context");
@ -4268,11 +4279,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
if (cmd.isAttach()) { if (cmd.isAttach()) {
String msg = "AttachIsoCommand(attach) failed due to " + VmwareHelper.getExceptionMessage(e); String msg = "AttachIsoCommand(attach) failed due to " + VmwareHelper.getExceptionMessage(e);
s_logger.error(msg, e); s_logger.error(msg, e);
return new Answer(cmd, false, msg); return new AttachIsoAnswer(cmd, false, msg);
} else { } else {
String msg = "AttachIsoCommand(detach) failed due to " + VmwareHelper.getExceptionMessage(e); String msg = "AttachIsoCommand(detach) failed due to " + VmwareHelper.getExceptionMessage(e);
s_logger.warn(msg, e); s_logger.warn(msg, e);
return new Answer(cmd, false, msg); return new AttachIsoAnswer(cmd, false, msg);
} }
} }
} }

View File

@ -33,11 +33,11 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.google.common.base.Strings;
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.google.common.base.Strings;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.vmware.vim25.HostHostBusAdapter; import com.vmware.vim25.HostHostBusAdapter;
import com.vmware.vim25.HostInternetScsiHba; import com.vmware.vim25.HostInternetScsiHba;
@ -71,6 +71,7 @@ import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
@ -93,7 +94,6 @@ import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO; import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.NetworkDetails; import com.cloud.hypervisor.vmware.mo.NetworkDetails;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.resource.VmwareResource; import com.cloud.hypervisor.vmware.resource.VmwareResource;
@ -1525,7 +1525,7 @@ public class VmwareStorageProcessor implements StorageProcessor {
int isoNameStartPos = isoPath.lastIndexOf('/'); int isoNameStartPos = isoPath.lastIndexOf('/');
String isoFileName = isoPath.substring(isoNameStartPos + 1); String isoFileName = isoPath.substring(isoNameStartPos + 1);
String isoStorePathFromRoot = isoPath.substring(storeUrl.length(), isoNameStartPos); String isoStorePathFromRoot = isoPath.substring(storeUrl.length() + 1, isoNameStartPos);
// TODO, check if iso is already attached, or if there is a previous // TODO, check if iso is already attached, or if there is a previous
// attachment // attachment

View File

@ -47,7 +47,7 @@ public final class CitrixPrepareForMigrationCommandWrapper extends CommandWrappe
String configDriveLabel = vm.getConfigDriveLabel(); String configDriveLabel = vm.getConfigDriveLabel();
if (configDriveLabel == null) { if (configDriveLabel == null) {
configDriveLabel = "config"; configDriveLabel = "config-2";
} }
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {

View File

@ -19,7 +19,39 @@
package com.cloud.network.element; package com.cloud.network.element;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import net.nuage.vsp.acs.client.api.model.VspAclRule;
import net.nuage.vsp.acs.client.api.model.VspDhcpDomainOption;
import net.nuage.vsp.acs.client.api.model.VspNetwork;
import net.nuage.vsp.acs.client.api.model.VspStaticNat;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.network.ExternalNetworkDeviceManager;
import org.apache.cloudstack.resourcedetail.VpcDetailVO;
import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupCommand;
@ -56,7 +88,9 @@ import com.cloud.network.dao.FirewallRulesCidrsDao;
import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkServiceMapDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.network.manager.NuageVspManager; import com.cloud.network.manager.NuageVspManager;
@ -95,33 +129,6 @@ import com.cloud.vm.ReservationContext;
import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicDao;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import net.nuage.vsp.acs.client.api.model.VspAclRule;
import net.nuage.vsp.acs.client.api.model.VspDhcpDomainOption;
import net.nuage.vsp.acs.client.api.model.VspNetwork;
import net.nuage.vsp.acs.client.api.model.VspStaticNat;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.network.ExternalNetworkDeviceManager;
import org.apache.cloudstack.resourcedetail.VpcDetailVO;
import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class NuageVspElement extends AdapterBase implements ConnectivityProvider, IpDeployer, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, public class NuageVspElement extends AdapterBase implements ConnectivityProvider, IpDeployer, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider,
DhcpServiceProvider, ResourceStateAdapter, VpcProvider, NetworkACLServiceProvider { DhcpServiceProvider, ResourceStateAdapter, VpcProvider, NetworkACLServiceProvider {
@ -159,6 +166,8 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider
@Inject @Inject
NetworkServiceMapDao _ntwkSrvcDao; NetworkServiceMapDao _ntwkSrvcDao;
@Inject @Inject
NetworkDao _networkDao;
@Inject
DomainDao _domainDao; DomainDao _domainDao;
@Inject @Inject
IPAddressDao _ipAddressDao; IPAddressDao _ipAddressDao;
@ -267,6 +276,13 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider
return false; return false;
} }
if (!_nuageVspEntityBuilder.usesVirtualRouter(offering.getId())) {
// Update broadcast uri if VR is no longer used
NetworkVO networkToUpdate = _networkDao.findById(network.getId());
String broadcastUriStr = networkToUpdate.getUuid() + "/null";
networkToUpdate.setBroadcastUri(Networks.BroadcastDomainType.Vsp.toUri(broadcastUriStr));
_networkDao.update(network.getId(), networkToUpdate);
}
VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network); VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network);
List<VspAclRule> ingressFirewallRules = getFirewallRulesToApply(network, FirewallRule.TrafficType.Ingress); List<VspAclRule> ingressFirewallRules = getFirewallRulesToApply(network, FirewallRule.TrafficType.Ingress);
@ -547,10 +563,11 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider
List<VlanVO> vlans = _vlanDao.listByZone(newVlan.getDataCenterId()); List<VlanVO> vlans = _vlanDao.listByZone(newVlan.getDataCenterId());
if (CollectionUtils.isNotEmpty(vlans)) { if (CollectionUtils.isNotEmpty(vlans)) {
boolean newVlanUnderlay = NuageVspUtil.isUnderlayEnabledForVlan(_vlanDetailsDao, newVlan); boolean newVlanUnderlay = NuageVspUtil.isUnderlayEnabledForVlan(_vlanDetailsDao, newVlan);
final String newCidr = NetUtils.getCidrFromGatewayAndNetmask(newVlan.getVlanGateway(), newVlan.getVlanNetmask());
for (VlanVO vlan : vlans) { for (VlanVO vlan : vlans) {
if (vlan.getId() == newVlan.getId()) continue; if (vlan.getId() == newVlan.getId()) continue;
final String newCidr = NetUtils.getCidrFromGatewayAndNetmask(newVlan.getVlanGateway(), newVlan.getVlanNetmask());
final String existingCidr = NetUtils.getCidrFromGatewayAndNetmask(vlan.getVlanGateway(), vlan.getVlanNetmask()); final String existingCidr = NetUtils.getCidrFromGatewayAndNetmask(vlan.getVlanGateway(), vlan.getVlanNetmask());
NetUtils.SupersetOrSubset supersetOrSubset = NetUtils.isNetowrkASubsetOrSupersetOfNetworkB(newCidr, existingCidr); NetUtils.SupersetOrSubset supersetOrSubset = NetUtils.isNetowrkASubsetOrSupersetOfNetworkB(newCidr, existingCidr);
@ -635,7 +652,7 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider
@Override @Override
public boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { public boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
List<VpcOfferingServiceMapVO> vpcOfferingServices = _vpcOfferingSrvcDao.listByVpcOffId(vpc.getVpcOfferingId()); List<VpcOfferingServiceMapVO> vpcOfferingServices = _vpcOfferingSrvcDao.listByVpcOffId(vpc.getVpcOfferingId());
Multimap<Service, Provider> supportedVpcServices = NuageVspManagerImpl.NUAGE_VSP_VPC_SERVICE_MAP; Multimap<Service, Provider> supportedVpcServices = NuageVspManagerImpl.SUPPORTED_NUAGE_VSP_VPC_SERVICE_MAP;
for (VpcOfferingServiceMapVO vpcOfferingService : vpcOfferingServices) { for (VpcOfferingServiceMapVO vpcOfferingService : vpcOfferingServices) {
Network.Service service = Network.Service.getService(vpcOfferingService.getService()); Network.Service service = Network.Service.getService(vpcOfferingService.getService());
if (!supportedVpcServices.containsKey(service)) { if (!supportedVpcServices.containsKey(service)) {

View File

@ -61,6 +61,7 @@ import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.dc.DataCenterDetailVO; import com.cloud.dc.DataCenterDetailVO;
import com.cloud.dc.VlanVO; import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.DataCenterDetailsDao; import com.cloud.dc.dao.DataCenterDetailsDao;
import com.cloud.dc.dao.VlanDetailsDao;
import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeployDestination;
import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlan;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
@ -88,6 +89,7 @@ import com.cloud.user.Account;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.util.NuageVspEntityBuilder; import com.cloud.util.NuageVspEntityBuilder;
import com.cloud.util.NuageVspUtil;
import com.cloud.utils.StringUtils; import com.cloud.utils.StringUtils;
import com.cloud.utils.db.DB; import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
@ -130,6 +132,8 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ
NetworkOrchestrationService _networkOrchestrationService; NetworkOrchestrationService _networkOrchestrationService;
@Inject @Inject
DataCenterDetailsDao _dcDetailsDao; DataCenterDetailsDao _dcDetailsDao;
@Inject
VlanDetailsDao _vlanDetailsDao;
public NuageVspGuestNetworkGuru() { public NuageVspGuestNetworkGuru() {
super(); super();
@ -299,6 +303,24 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ
} }
VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(implemented, true); VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(implemented, true);
if (vspNetwork.isShared()) {
Boolean previousUnderlay= null;
for (VlanVO vlan : _vlanDao.listVlansByNetworkId(networkId)) {
boolean underlay = NuageVspUtil.isUnderlayEnabledForVlan(_vlanDetailsDao, vlan);
if (previousUnderlay == null || underlay == previousUnderlay) {
previousUnderlay = underlay;
} else {
throw new CloudRuntimeException("Mixed values for the underlay flag for IP ranges in the same subnet is not supported");
}
}
if (previousUnderlay != null) {
vspNetwork = new VspNetwork.Builder().fromObject(vspNetwork)
.vlanUnderlay(previousUnderlay)
.build();
}
}
String tenantId = context.getDomain().getName() + "-" + context.getAccount().getAccountId(); String tenantId = context.getDomain().getName() + "-" + context.getAccount().getAccountId();
String broadcastUriStr = implemented.getUuid() + "/" + vspNetwork.getVirtualRouterIp(); String broadcastUriStr = implemented.getUuid() + "/" + vspNetwork.getVirtualRouterIp();
implemented.setBroadcastUri(Networks.BroadcastDomainType.Vsp.toUri(broadcastUriStr)); implemented.setBroadcastUri(Networks.BroadcastDomainType.Vsp.toUri(broadcastUriStr));
@ -495,6 +517,16 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ
//update the extra DHCP options //update the extra DHCP options
} }
// Update broadcast Uri to enable VR ip update
if (!network.getBroadcastUri().getPath().substring(1).equals(vspNetwork.getVirtualRouterIp())) {
NetworkVO networkToUpdate = _networkDao.findById(network.getId());
String broadcastUriStr = networkToUpdate.getUuid() + "/" + vspNetwork.getVirtualRouterIp();
networkToUpdate.setBroadcastUri(Networks.BroadcastDomainType.Vsp.toUri(broadcastUriStr));
_networkDao.update(network.getId(), networkToUpdate);
if (network instanceof NetworkVO) {
((NetworkVO) network).setBroadcastUri(networkToUpdate.getBroadcastUri());
}
}
nic.setBroadcastUri(network.getBroadcastUri()); nic.setBroadcastUri(network.getBroadcastUri());
nic.setIsolationUri(network.getBroadcastUri()); nic.setIsolationUri(network.getBroadcastUri());
@ -586,8 +618,15 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ
} }
} }
private boolean isServiceProvidedByVR(Network network, Network.Service service ) {
return (_networkModel.areServicesSupportedInNetwork(network.getId(), service) &&
( _networkModel.isProviderSupportServiceInNetwork(network.getId(), service, Network.Provider.VirtualRouter) ||
_networkModel.isProviderSupportServiceInNetwork(network.getId(), service, Network.Provider.VPCVirtualRouter)));
}
private void checkMultipleSubnetsCombinedWithUseData(Network network) { private void checkMultipleSubnetsCombinedWithUseData(Network network) {
if (_ntwkOfferingSrvcDao.listServicesForNetworkOffering(network.getNetworkOfferingId()).contains(Network.Service.UserData.getName())) { if (isServiceProvidedByVR(network, Network.Service.UserData)) {
List<VlanVO> vlanVOs = _vlanDao.listVlansByNetworkId(network.getId()); List<VlanVO> vlanVOs = _vlanDao.listVlansByNetworkId(network.getId());
if (vlanVOs.stream() if (vlanVOs.stream()
.map(VlanVO::getVlanGateway) .map(VlanVO::getVlanGateway)

View File

@ -161,7 +161,8 @@ public class NuageVspManagerImpl extends ManagerBase implements NuageVspManager,
private static final Logger s_logger = Logger.getLogger(NuageVspManagerImpl.class); private static final Logger s_logger = Logger.getLogger(NuageVspManagerImpl.class);
public static final Multimap<Network.Service, Network.Provider> NUAGE_VSP_VPC_SERVICE_MAP; public static final Multimap<Network.Service, Network.Provider> DEFAULT_NUAGE_VSP_VPC_SERVICE_MAP;
public static final Multimap<Network.Service, Network.Provider> SUPPORTED_NUAGE_VSP_VPC_SERVICE_MAP;
private static final ConfigKey[] NUAGE_VSP_CONFIG_KEYS = new ConfigKey<?>[] { NuageVspConfigDns, NuageVspDnsExternal, NuageVspConfigGateway, private static final ConfigKey[] NUAGE_VSP_CONFIG_KEYS = new ConfigKey<?>[] { NuageVspConfigDns, NuageVspDnsExternal, NuageVspConfigGateway,
NuageVspSharedNetworkDomainTemplateName, NuageVspVpcDomainTemplateName, NuageVspIsolatedNetworkDomainTemplateName }; NuageVspSharedNetworkDomainTemplateName, NuageVspVpcDomainTemplateName, NuageVspIsolatedNetworkDomainTemplateName };
@ -216,8 +217,11 @@ public class NuageVspManagerImpl extends ManagerBase implements NuageVspManager,
static { static {
Set<Network.Provider> nuageVspProviders = ImmutableSet.of(Network.Provider.NuageVsp); Set<Network.Provider> nuageVspProviders = ImmutableSet.of(Network.Provider.NuageVsp);
Set<Network.Provider> vrProviders = ImmutableSet.of(Network.Provider.VPCVirtualRouter); Set<Network.Provider> vrProviders = ImmutableSet.of(Network.Provider.VPCVirtualRouter);
Set<Network.Provider> lbProviders = ImmutableSet.of(Network.Provider.InternalLbVm); Set<Network.Provider> defaultLbProviders = ImmutableSet.of(Network.Provider.InternalLbVm);
NUAGE_VSP_VPC_SERVICE_MAP = ImmutableMultimap.<Network.Service, Network.Provider>builder() Set<Network.Provider> supportedLbProviders = ImmutableSet.of(Network.Provider.InternalLbVm);
Set<Network.Provider> supportedUserDataProviders = ImmutableSet.of(Network.Provider.VPCVirtualRouter, Network.Provider.ConfigDrive);
DEFAULT_NUAGE_VSP_VPC_SERVICE_MAP = ImmutableMultimap.<Network.Service, Network.Provider>builder()
.putAll(Network.Service.Connectivity, nuageVspProviders) .putAll(Network.Service.Connectivity, nuageVspProviders)
.putAll(Network.Service.Gateway, nuageVspProviders) .putAll(Network.Service.Gateway, nuageVspProviders)
.putAll(Network.Service.Dhcp, nuageVspProviders) .putAll(Network.Service.Dhcp, nuageVspProviders)
@ -225,9 +229,15 @@ public class NuageVspManagerImpl extends ManagerBase implements NuageVspManager,
.putAll(Network.Service.SourceNat, nuageVspProviders) .putAll(Network.Service.SourceNat, nuageVspProviders)
.putAll(Network.Service.NetworkACL, nuageVspProviders) .putAll(Network.Service.NetworkACL, nuageVspProviders)
.putAll(Network.Service.UserData, vrProviders) .putAll(Network.Service.UserData, vrProviders)
.putAll(Network.Service.Lb, lbProviders) .putAll(Network.Service.Lb, defaultLbProviders)
.putAll(Network.Service.Dns, vrProviders) .putAll(Network.Service.Dns, vrProviders)
.build(); .build();
Multimap<Network.Service, Network.Provider> builder = HashMultimap.create(DEFAULT_NUAGE_VSP_VPC_SERVICE_MAP);
builder.putAll(Network.Service.UserData, supportedUserDataProviders);
builder.putAll(Network.Service.Lb, supportedLbProviders);
SUPPORTED_NUAGE_VSP_VPC_SERVICE_MAP = ImmutableMultimap.copyOf(builder);
} }
private Listener _nuageVspResourceListener; private Listener _nuageVspResourceListener;
@ -1193,9 +1203,9 @@ public class NuageVspManagerImpl extends ManagerBase implements NuageVspManager,
s_logger.debug("Creating default Nuage VPC offering " + nuageVPCOfferingName); s_logger.debug("Creating default Nuage VPC offering " + nuageVPCOfferingName);
} }
createVpcOffering(nuageVPCOfferingName, nuageVPCOfferingDisplayText, NUAGE_VSP_VPC_SERVICE_MAP, true, VpcOffering.State.Enabled, null); createVpcOffering(nuageVPCOfferingName, nuageVPCOfferingDisplayText, DEFAULT_NUAGE_VSP_VPC_SERVICE_MAP, true, VpcOffering.State.Enabled, null);
} else { } else {
updateVpcOffering(offering, NUAGE_VSP_VPC_SERVICE_MAP); updateVpcOffering(offering, DEFAULT_NUAGE_VSP_VPC_SERVICE_MAP);
} }
} }
}); });

View File

@ -22,9 +22,11 @@ package com.cloud.network.resource;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.google.common.base.Strings;
import net.nuage.vsp.acs.client.api.NuageVspAclClient; import net.nuage.vsp.acs.client.api.NuageVspAclClient;
import net.nuage.vsp.acs.client.api.NuageVspApiClient; import net.nuage.vsp.acs.client.api.NuageVspApiClient;
import net.nuage.vsp.acs.client.api.NuageVspElementClient; import net.nuage.vsp.acs.client.api.NuageVspElementClient;
@ -36,10 +38,6 @@ import net.nuage.vsp.acs.client.common.RequestType;
import net.nuage.vsp.acs.client.common.model.NuageVspEntity; import net.nuage.vsp.acs.client.common.model.NuageVspEntity;
import net.nuage.vsp.acs.client.exception.NuageVspException; import net.nuage.vsp.acs.client.exception.NuageVspException;
import org.apache.log4j.Logger;
import com.google.common.base.Strings;
import com.cloud.agent.IAgentControl; import com.cloud.agent.IAgentControl;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;

View File

@ -227,7 +227,6 @@ public class NuageVspEntityBuilder {
if (networkOffering.getGuestType() == Network.GuestType.Shared) { if (networkOffering.getGuestType() == Network.GuestType.Shared) {
List<VlanVO> vlans = _vlanDao.listVlansByNetworkIdIncludingRemoved(network.getId()); List<VlanVO> vlans = _vlanDao.listVlansByNetworkIdIncludingRemoved(network.getId());
List<VspAddressRange> vspAddressRanges = List<VspAddressRange> vspAddressRanges =
vlans.stream() vlans.stream()
.map(vlan -> new VspAddressRange.Builder().gateway(vlan.getVlanGateway()).netmask(vlan.getVlanNetmask()).build()) .map(vlan -> new VspAddressRange.Builder().gateway(vlan.getVlanGateway()).netmask(vlan.getVlanNetmask()).build())
@ -290,9 +289,11 @@ public class NuageVspEntityBuilder {
.findFirst() .findFirst()
.get(); .get();
boolean underlayEnabled = NuageVspUtil.isUnderlayEnabledForVlan(_vlanDetailsDao, matchingVlan);
return new VspNetwork.Builder().fromObject(vspNetwork) return new VspNetwork.Builder().fromObject(vspNetwork)
.gateway(matchingVlan.getVlanGateway()) .gateway(matchingVlan.getVlanGateway())
.cidr(NetUtils.getCidrFromGatewayAndNetmask(matchingVlan.getVlanGateway(), matchingVlan.getVlanNetmask())) .cidr(NetUtils.getCidrFromGatewayAndNetmask(matchingVlan.getVlanGateway(), matchingVlan.getVlanNetmask()))
.vlanUnderlay(underlayEnabled)
.build(); .build();
} }

View File

@ -60,7 +60,9 @@ import com.cloud.network.NuageVspDeviceVO;
import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkServiceMapDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.NuageVspDao; import com.cloud.network.dao.NuageVspDao;
import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.PhysicalNetworkVO;
@ -109,6 +111,7 @@ public class NuageVspElementTest extends NuageTest {
@Mock private DomainRouterDao _domainRouterDao; @Mock private DomainRouterDao _domainRouterDao;
@Mock private ResourceManager _resourceManager; @Mock private ResourceManager _resourceManager;
@Mock private ResourceTagDao _resourceTagDao; @Mock private ResourceTagDao _resourceTagDao;
@Mock private NetworkDao _networkDao;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -173,6 +176,11 @@ public class NuageVspElementTest extends NuageTest {
when(network.getDataCenterId()).thenReturn(NETWORK_ID); when(network.getDataCenterId()).thenReturn(NETWORK_ID);
when(_networkModel.isProviderForNetwork(Provider.NuageVsp, NETWORK_ID)).thenReturn(true); when(_networkModel.isProviderForNetwork(Provider.NuageVsp, NETWORK_ID)).thenReturn(true);
final NetworkVO networkVO = mock(NetworkVO.class);
when(network.getUuid()).thenReturn("aaaaaa");
when(_networkDao.findById(NETWORK_ID)).thenReturn(networkVO);
final NetworkOffering offering = mock(NetworkOffering.class); final NetworkOffering offering = mock(NetworkOffering.class);
when(offering.getId()).thenReturn(NETWORK_ID); when(offering.getId()).thenReturn(NETWORK_ID);
when(offering.getTrafficType()).thenReturn(TrafficType.Guest); when(offering.getTrafficType()).thenReturn(TrafficType.Guest);

View File

@ -335,6 +335,8 @@ public class NuageVspGuestNetworkGuruTest extends NuageTest {
when(reservationContext.getAccount()).thenReturn(networksAccount); when(reservationContext.getAccount()).thenReturn(networksAccount);
when(reservationContext.getDomain()).thenReturn(networksDomain); when(reservationContext.getDomain()).thenReturn(networksDomain);
when(_networkDao.findById(NETWORK_ID)).thenReturn(network);
_nuageVspGuestNetworkGuru.reserve(nicProfile, network, vmProfile, mock(DeployDestination.class), reservationContext); _nuageVspGuestNetworkGuru.reserve(nicProfile, network, vmProfile, mock(DeployDestination.class), reservationContext);
} }

View File

@ -96,8 +96,8 @@ public class NuageVspEntityBuilderTest extends NuageTest {
@Mock private NetworkDetailsDao _networkDetailsDao; @Mock private NetworkDetailsDao _networkDetailsDao;
@Mock private NetworkOfferingDao _networkOfferingDao; @Mock private NetworkOfferingDao _networkOfferingDao;
@Mock private NetworkOfferingServiceMapDao _networkOfferingServiceMapDao; @Mock private NetworkOfferingServiceMapDao _networkOfferingServiceMapDao;
@Mock private NicSecondaryIpDao _nicSecondaryIpDao;
@Mock private NicDao _nicDao; @Mock private NicDao _nicDao;
@Mock private NicSecondaryIpDao _nicSecondaryIpDao;
@Mock private VlanDao _vlanDao; @Mock private VlanDao _vlanDao;
@Mock private VlanDetailsDao _vlanDetailsDao; @Mock private VlanDetailsDao _vlanDetailsDao;
@Mock private VpcDao _vpcDao; @Mock private VpcDao _vpcDao;

View File

@ -61,4 +61,8 @@
<property name="name" value="VirtualRouter" /> <property name="name" value="VirtualRouter" />
</bean> </bean>
<bean id="ConfigDrive" class="com.cloud.network.element.ConfigDriveNetworkElement">
<property name="name" value="ConfigDrive" />
</bean>
</beans> </beans>

View File

@ -30,20 +30,19 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.Collections; import java.util.Collections;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiDBUtils;
import com.cloud.configuration.Config; import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ConfigurationManager;
@ -2349,14 +2348,14 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
final List<String[]> vmData = new ArrayList<String[]>(); final List<String[]> vmData = new ArrayList<String[]>();
if (userData != null) { if (userData != null) {
vmData.add(new String[]{"userdata", "user-data", new String(Base64.decodeBase64(userData),StringUtils.getPreferredCharset())}); vmData.add(new String[]{USERDATA_DIR, USERDATA_FILE, new String(Base64.decodeBase64(userData),StringUtils.getPreferredCharset())});
} }
vmData.add(new String[]{"metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering)}); vmData.add(new String[]{METATDATA_DIR, SERVICE_OFFERING_FILE, StringUtils.unicodeEscape(serviceOffering)});
vmData.add(new String[]{"metadata", "availability-zone", StringUtils.unicodeEscape(zoneName)}); vmData.add(new String[]{METATDATA_DIR, AVAILABILITY_ZONE_FILE, StringUtils.unicodeEscape(zoneName)});
vmData.add(new String[]{"metadata", "local-hostname", StringUtils.unicodeEscape(vmName)}); vmData.add(new String[]{METATDATA_DIR, LOCAL_HOSTNAME_FILE, StringUtils.unicodeEscape(vmName)});
vmData.add(new String[]{"metadata", "instance-id", vmName}); vmData.add(new String[]{METATDATA_DIR, INSTANCE_ID_FILE, vmName});
vmData.add(new String[]{"metadata", "vm-id", String.valueOf(vmId)}); vmData.add(new String[]{METATDATA_DIR, VM_ID_FILE, String.valueOf(vmId)});
vmData.add(new String[]{"metadata", "public-keys", publicKey}); vmData.add(new String[]{METATDATA_DIR, PUBLIC_KEYS_FILE, publicKey});
String cloudIdentifier = _configDao.getValue("cloud.identifier"); String cloudIdentifier = _configDao.getValue("cloud.identifier");
if (cloudIdentifier == null) { if (cloudIdentifier == null) {
@ -2364,7 +2363,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
} else { } else {
cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}"; cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}";
} }
vmData.add(new String[]{"metadata", "cloud-identifier", cloudIdentifier}); vmData.add(new String[]{METATDATA_DIR, CLOUD_IDENTIFIER_FILE, cloudIdentifier});
if (password != null && !password.isEmpty() && !password.equals("saved_password")) { if (password != null && !password.isEmpty() && !password.equals("saved_password")) {
@ -2385,10 +2384,10 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
BigInteger bigInt = new BigInteger(1, digest); BigInteger bigInt = new BigInteger(1, digest);
String hashtext = bigInt.toString(16); String hashtext = bigInt.toString(16);
vmData.add(new String[]{"password", "vm-password-md5checksum", hashtext}); vmData.add(new String[]{PASSWORD_DIR, PASSWORD_CHECKSUM_FILE, hashtext});
} }
vmData.add(new String[]{"password", "vm-password", password}); vmData.add(new String[]{PASSWORD_DIR, PASSWORD_FILE, password});
} }
return vmData; return vmData;

View File

@ -2896,6 +2896,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
//Add Internal Load Balancer element as a default network service provider //Add Internal Load Balancer element as a default network service provider
addDefaultInternalLbProviderToPhysicalNetwork(pNetwork.getId()); addDefaultInternalLbProviderToPhysicalNetwork(pNetwork.getId());
// Add the config drive provider
addConfigDriveToPhysicalNetwork(pNetwork.getId());
return pNetwork; return pNetwork;
} }
}); });
@ -4198,13 +4201,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
addProviderToPhysicalNetwork(physicalNetworkId, "BaremetalUserdataProvider", null, null); addProviderToPhysicalNetwork(physicalNetworkId, "BaremetalUserdataProvider", null, null);
} else if (dvo.getNetworkType() == NetworkType.Advanced) { } else if (dvo.getNetworkType() == NetworkType.Advanced) {
addProviderToPhysicalNetwork(physicalNetworkId, "BaremetalPxeProvider", null, null); addProviderToPhysicalNetwork(physicalNetworkId, "BaremetalPxeProvider", null, null);
enableBaremetalProvider("BaremetalPxeProvider"); enableProvider("BaremetalPxeProvider");
} }
return null; return null;
} }
private void enableBaremetalProvider(String providerName) { private void enableProvider(String providerName) {
QueryBuilder<PhysicalNetworkServiceProviderVO> q = QueryBuilder.create(PhysicalNetworkServiceProviderVO.class); QueryBuilder<PhysicalNetworkServiceProviderVO> q = QueryBuilder.create(PhysicalNetworkServiceProviderVO.class);
q.and(q.entity().getProviderName(), SearchCriteria.Op.EQ, providerName); q.and(q.entity().getProviderName(), SearchCriteria.Op.EQ, providerName);
PhysicalNetworkServiceProviderVO provider = q.find(); PhysicalNetworkServiceProviderVO provider = q.find();
@ -4212,6 +4215,22 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
_pNSPDao.update(provider.getId(), provider); _pNSPDao.update(provider.getId(), provider);
} }
private PhysicalNetworkServiceProvider addConfigDriveToPhysicalNetwork(long physicalNetworkId) {
PhysicalNetworkVO pvo = _physicalNetworkDao.findById(physicalNetworkId);
DataCenterVO dvo = _dcDao.findById(pvo.getDataCenterId());
if (dvo.getNetworkType() == NetworkType.Advanced) {
Provider provider = Network.Provider.getProvider("ConfigDrive");
if (provider == null) {
return null;
}
addProviderToPhysicalNetwork(physicalNetworkId, Provider.ConfigDrive.getName(), null, null);
enableProvider(Provider.ConfigDrive.getName());
}
return null;
}
protected boolean isNetworkSystem(Network network) { protected boolean isNetworkSystem(Network network) {
NetworkOffering no = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId()); NetworkOffering no = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId());
if (no.isSystemOnly()) { if (no.isSystemOnly()) {

View File

@ -0,0 +1,424 @@
// 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.network.element;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.AttachIsoAnswer;
import com.cloud.agent.api.AttachIsoCommand;
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.UnsupportedServiceException;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.network.Network;
import com.cloud.network.Network.Capability;
import com.cloud.network.Network.Provider;
import com.cloud.network.Network.Service;
import com.cloud.network.NetworkMigrationResponder;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.PhysicalNetworkServiceProvider;
import com.cloud.network.dao.NetworkDao;
import com.cloud.offering.NetworkOffering;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage;
import com.cloud.storage.Volume;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.fsm.StateListener;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
public class ConfigDriveNetworkElement extends AdapterBase implements NetworkElement, UserDataServiceProvider,
StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualMachine>, NetworkMigrationResponder {
private static final Logger s_logger = Logger.getLogger(ConfigDriveNetworkElement.class);
private static final Map<Service, Map<Capability, String>> capabilities = setCapabilities();
@Inject
NetworkDao _networkConfigDao;
@Inject
NetworkModel _networkMgr;
@Inject
UserVmManager _userVmMgr;
@Inject
UserVmDao _userVmDao;
@Inject
UserVmDetailsDao _userVmDetailsDao;
@Inject
DomainRouterDao _routerDao;
@Inject
ConfigurationManager _configMgr;
@Inject
DataCenterDao _dcDao;
@Inject
AgentManager _agentManager;
@Inject
ServiceOfferingDao _serviceOfferingDao;
@Inject
NetworkModel _networkModel;
@Inject
GuestOSCategoryDao _guestOSCategoryDao;
@Inject
GuestOSDao _guestOSDao;
@Inject
HostDao _hostDao;
@Inject
DataStoreManager _dataStoreMgr;
@Inject
EndPointSelector _ep;
@Inject
VolumeOrchestrationService _volumeMgr;
public final static String CONFIGDRIVEFILENAME = "configdrive.iso";
public final static String CONFIGDRIVEDIR= "ConfigDrive";
public final static Integer CONFIGDRIVEDISKSEQ= new Integer(4);
private boolean canHandle(TrafficType trafficType) {
return trafficType.equals(TrafficType.Guest);
}
@Override
public boolean start() {
VirtualMachine.State.getStateMachine().registerListener(this);
return super.start();
}
@Override
public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException,
InsufficientCapacityException {
return canHandle(offering.getTrafficType());
}
@Override
public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
InsufficientCapacityException, ResourceUnavailableException {
return true;
}
@Override
public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) {
if (!nic.isDefaultNic()) {
return true;
}
// Remove form secondary storage
DataStore secondaryStore = _dataStoreMgr.getImageStore(network.getDataCenterId());
String isoFile = "/" + CONFIGDRIVEDIR + "/" + vm.getInstanceName()+ "/" + CONFIGDRIVEFILENAME;
HandleConfigDriveIsoCommand deleteCommand = new HandleConfigDriveIsoCommand(vm.getVmData(),
vm.getConfigDriveLabel(), secondaryStore.getTO(), isoFile, false, false);
// Delete the ISO on the secondary store
EndPoint endpoint = _ep.select(secondaryStore);
if (endpoint == null) {
s_logger.error(String.format("Secondary store: %s not available", secondaryStore.getName()));
return false;
}
Answer answer = endpoint.sendMessage(deleteCommand);
return answer.getResult();
}
@Override
public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
return true; // assume that the agent will remove userdata etc
}
@Override
public boolean destroy(Network config, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
return true; // assume that the agent will remove userdata etc
}
@Override
public Provider getProvider() {
return Provider.ConfigDrive;
}
@Override
public Map<Service, Map<Capability, String>> getCapabilities() {
return capabilities;
}
private static Map<Service, Map<Capability, String>> setCapabilities() {
Map<Service, Map<Capability, String>> capabilities = new HashMap<>();
capabilities.put(Service.UserData, null);
return capabilities;
}
@Override
public boolean isReady(PhysicalNetworkServiceProvider provider) {
return true;
}
@Override
public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
return true;
}
@Override
public boolean canEnableIndividualServices() {
return false;
}
@Override
public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context)
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
UserVmDetailVO vmDetailSshKey = _userVmDetailsDao.findDetail(profile.getId(), "SSH.PublicKey");
return (canHandle(network.getTrafficType()) && updateConfigDrive(profile,
(vmDetailSshKey!=null?vmDetailSshKey.getValue():null)))
&& updateConfigDriveIso(network, profile, dest.getHost(), false);
}
@Override
public boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile profile) throws ResourceUnavailableException {
if (!(canHandle(network.getTrafficType()) && updateConfigDrive(profile, (String) profile.getParameter(VirtualMachineProfile.Param.VmSshPubKey)))) return false;
return updateConfigDriveIso(network, profile, true);
}
@Override
public boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String sshPublicKey) throws ResourceUnavailableException {
if (!(canHandle(network.getTrafficType()) && updateConfigDrive(vm, sshPublicKey))) return false;
return updateConfigDriveIso(network, vm, true);
}
@Override
public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile profile) throws ResourceUnavailableException {
if (!(canHandle(network.getTrafficType()) && updateConfigDrive(profile, (String) profile.getParameter(VirtualMachineProfile.Param.VmSshPubKey)))) return false;
return updateConfigDriveIso(network, profile, true);
}
@Override
public boolean verifyServicesCombination(Set<Service> services) {
return true;
}
@Override
public boolean preStateTransitionEvent(VirtualMachine.State oldState, VirtualMachine.Event event, VirtualMachine.State newState, VirtualMachine vo, boolean status, Object opaque) {
return true;
}
@Override
public boolean postStateTransitionEvent(StateMachine2.Transition<VirtualMachine.State, VirtualMachine.Event> transition, VirtualMachine vo, boolean status, Object opaque) {
if (transition.getToState().equals(VirtualMachine.State.Expunging) && transition.getEvent().equals(VirtualMachine.Event.ExpungeOperation)) {
Nic nic = _networkModel.getDefaultNic(vo.getId());
try {
if (nic != null) {
final Network network = _networkMgr.getNetwork(nic.getNetworkId());
final UserDataServiceProvider userDataUpdateProvider = _networkModel.getUserDataUpdateProvider(network);
final Provider provider = userDataUpdateProvider.getProvider();
if (provider.equals(Provider.ConfigDrive)) {
// Delete config drive ISO on destroy
DataStore secondaryStore = _dataStoreMgr.getImageStore(vo.getDataCenterId());
String isoFile = "/" + CONFIGDRIVEDIR + "/" + vo.getInstanceName() + "/" + CONFIGDRIVEFILENAME;
HandleConfigDriveIsoCommand deleteCommand = new HandleConfigDriveIsoCommand(null,
null, secondaryStore.getTO(), isoFile, false, false);
EndPoint endpoint = _ep.select(secondaryStore);
if (endpoint == null) {
s_logger.error(String.format("Secondary store: %s not available", secondaryStore.getName()));
return false;
}
Answer answer = endpoint.sendMessage(deleteCommand);
if (!answer.getResult()) {
s_logger.error(String.format("Update ISO failed, details: %s", answer.getDetails()));
return false;
}
}
}
} catch (UnsupportedServiceException usse) {}
}
return true;
}
@Override
public boolean prepareMigration(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) {
if (nic.isDefaultNic() && _networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) {
s_logger.trace(String.format("[prepareMigration] for vm: %s", vm.getInstanceName()));
DataStore secondaryStore = _dataStoreMgr.getImageStore(network.getDataCenterId());
configureConfigDriveDisk(vm, secondaryStore);
return false;
}
else return true;
}
@Override
public void rollbackMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
}
@Override
public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
}
private boolean updateConfigDriveIso(Network network, VirtualMachineProfile profile, boolean update) throws ResourceUnavailableException {
return updateConfigDriveIso(network, profile, null, update);
}
private boolean updateConfigDriveIso(Network network, VirtualMachineProfile profile, Host host, boolean update) throws ResourceUnavailableException {
Integer deviceKey = null;
Long hostId;
if (host == null) {
hostId = (profile.getVirtualMachine().getHostId() == null ? profile.getVirtualMachine().getLastHostId(): profile.getVirtualMachine().getHostId());
} else {
hostId = host.getId();
}
DataStore secondaryStore = _dataStoreMgr.getImageStore(network.getDataCenterId());
// Detach the existing ISO file if the machine is running
if (update && profile.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
s_logger.debug("Detach config drive ISO for vm " + profile.getInstanceName() + " in host " + _hostDao.findById(hostId));
deviceKey = detachIso(secondaryStore, profile.getInstanceName(), hostId);
}
// Create/Update the iso on the secondary store
s_logger.debug(String.format("%s config drive ISO for vm %s in host %s",
(update?"update":"create"), profile.getInstanceName(), _hostDao.findById(hostId).getName()));
EndPoint endpoint = _ep.select(secondaryStore);
if (endpoint == null )
throw new ResourceUnavailableException(String.format("%s failed, secondary store not available",
(update?"Update":"Create")),secondaryStore.getClass(),secondaryStore.getId());
String isoPath = CONFIGDRIVEDIR + "/" + profile.getInstanceName() + "/" + CONFIGDRIVEFILENAME;
HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(profile.getVmData(),
profile.getConfigDriveLabel(), secondaryStore.getTO(), isoPath, true, update);
Answer createIsoAnswer = endpoint.sendMessage(configDriveIsoCommand);
if (!createIsoAnswer.getResult()) {
throw new ResourceUnavailableException(String.format("%s ISO failed, details: %s",
(update?"Update":"Create"), createIsoAnswer.getDetails()),ConfigDriveNetworkElement.class,0L);
}
configureConfigDriveDisk(profile, secondaryStore);
// Re-attach the ISO if the machine is running
if (update && profile.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
s_logger.debug("Re-attach config drive ISO for vm " + profile.getInstanceName() + " in host " + _hostDao.findById(hostId));
attachIso(secondaryStore, profile.getInstanceName(), hostId, deviceKey);
}
return true;
}
private void configureConfigDriveDisk(VirtualMachineProfile profile, DataStore secondaryStore) {
boolean isoAvailable = false;
String isoPath = CONFIGDRIVEDIR + "/" + profile.getInstanceName() + "/" + CONFIGDRIVEFILENAME;
for (DiskTO dataTo : profile.getDisks()) {
if (dataTo.getPath().equals(isoPath)) {
isoAvailable = true;
break;
}
}
if (!isoAvailable) {
TemplateObjectTO dataTO = new TemplateObjectTO();
dataTO.setDataStore(secondaryStore.getTO());
dataTO.setUuid(profile.getUuid());
dataTO.setPath(isoPath);
dataTO.setFormat(Storage.ImageFormat.ISO);
profile.addDisk(new DiskTO(dataTO, CONFIGDRIVEDISKSEQ.longValue(), isoPath, Volume.Type.ISO));
}
}
private boolean updateConfigDrive(VirtualMachineProfile profile, String publicKey) {
UserVmVO vm = _userVmDao.findById(profile.getId());
if (vm.getType() != VirtualMachine.Type.User) {
return false;
}
// add/update userdata and/or password info into vm profile
Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
if (defaultNic != null) {
final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText();
final String zoneName = _dcDao.findById(vm.getDataCenterId()).getName();
boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
List<String[]> vmData = _networkModel.generateVmData(vm.getUserData(), serviceOffering, zoneName, vm.getInstanceName(), vm.getId(),
publicKey, (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
profile.setVmData(vmData);
profile.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value());
}
return true;
}
private Integer detachIso (DataStore secondaryStore, String instanceName, Long hostId) throws ResourceUnavailableException {
String isoPath = CONFIGDRIVEDIR + "/" + instanceName + "/" + CONFIGDRIVEFILENAME;
AttachIsoCommand isoCommand = new AttachIsoCommand(instanceName, secondaryStore.getUri() + "/" + isoPath, false, CONFIGDRIVEDISKSEQ, true);
isoCommand.setStoreUrl(secondaryStore.getUri());
Answer attachIsoAnswer = null;
try {
attachIsoAnswer = _agentManager.send(hostId, isoCommand);
} catch (OperationTimedoutException e) {
throw new ResourceUnavailableException("Detach ISO failed: " + e.getMessage(), ConfigDriveNetworkElement.class, 0L);
}
if (!attachIsoAnswer.getResult()) {
throw new ResourceUnavailableException("Detach ISO failed: " + attachIsoAnswer.getDetails(), ConfigDriveNetworkElement.class, 0L);
}
if (attachIsoAnswer instanceof AttachIsoAnswer) {
return ((AttachIsoAnswer)attachIsoAnswer).getDeviceKey();
} else {
return CONFIGDRIVEDISKSEQ;
}
}
private void attachIso (DataStore secondaryStore, String instanceName, Long hostId, Integer deviceKey) throws ResourceUnavailableException {
String isoPath = CONFIGDRIVEDIR + "/" + instanceName + "/" + CONFIGDRIVEFILENAME;
AttachIsoCommand isoCommand = new AttachIsoCommand(instanceName, secondaryStore.getUri() + "/" + isoPath, true);
isoCommand.setStoreUrl(secondaryStore.getUri());
isoCommand.setDeviceKey(deviceKey);
Answer attachIsoAnswer = null;
try {
attachIsoAnswer = _agentManager.send(hostId, isoCommand);
} catch (OperationTimedoutException e) {
throw new ResourceUnavailableException("Attach ISO failed: " + e.getMessage() ,ConfigDriveNetworkElement.class,0L);
}
if (!attachIsoAnswer.getResult()) {
throw new ResourceUnavailableException("Attach ISO failed: " + attachIsoAnswer.getDetails(),ConfigDriveNetworkElement.class,0L);
}
}
}

View File

@ -33,7 +33,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
@ -48,7 +47,6 @@ import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import com.cloud.configuration.Config; import com.cloud.configuration.Config;
import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
@ -206,7 +204,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
private List<VpcProvider> vpcElements = null; private List<VpcProvider> vpcElements = null;
private final List<Service> nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall); private final List<Service> nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall);
private final List<Provider> supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler, private final List<Provider> supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler,
Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.NuageVsp, Provider.BigSwitchBcf); Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.NuageVsp, Provider.BigSwitchBcf, Provider.ConfigDrive);
int _cleanupInterval; int _cleanupInterval;
int _maxNetworks; int _maxNetworks;

View File

@ -16,6 +16,8 @@
// under the License. // under the License.
package com.cloud.vm; package com.cloud.vm;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@ -3996,6 +3998,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
protected String validateUserData(String userData, HTTPMethod httpmethod) { protected String validateUserData(String userData, HTTPMethod httpmethod) {
byte[] decodedUserData = null; byte[] decodedUserData = null;
if (userData != null) { if (userData != null) {
if (userData.contains("%")) {
try {
userData = URLDecoder.decode(userData, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InvalidParameterValueException("Url decoding of userdata failed.");
}
}
if (!Base64.isBase64(userData)) { if (!Base64.isBase64(userData)) {
throw new InvalidParameterValueException("User data is not base64 encoded"); throw new InvalidParameterValueException("User data is not base64 encoded");
} }

View File

@ -0,0 +1,272 @@
// 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.network.element;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.xerces.impl.dv.util.Base64;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import com.google.common.collect.Maps;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.network.Network;
import com.cloud.network.NetworkModelImpl;
import com.cloud.network.Networks;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkServiceMapDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.GuestOSCategoryVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.utils.fsm.StateListener;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.VirtualMachineProfileImpl;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
public class ConfigDriveNetworkElementTest {
public static final String CLOUD_ID = "xx";
public static final String PUBLIC_KEY = "publicKey";
public static final String PASSWORD = "password";
public static final long NETWORK_ID = 1L;
private final long DATACENTERID = NETWORK_ID;
private final String ZONENAME = "zone1";
private final String VMINSTANCENAME = "vm_name";
private final String VMOFFERING = "custom_instance";
private final long VMID = 30L;
private final String VMUSERDATA = "userdata";
private final long SOID = 31L;
private final long HOSTID = NETWORK_ID;
private final String HOSTNAME = "host1";
@Mock private ConfigurationDao _configDao;
@Mock private DataCenterDao _dcDao;
@Mock private DataStoreManager _dataStoreMgr;
@Mock private GuestOSCategoryDao _guestOSCategoryDao ;
@Mock private GuestOSDao _guestOSDao;
@Mock private HostDao _hostDao;
@Mock private ServiceOfferingDao _serviceOfferingDao;
@Mock private UserVmDao _vmDao;
@Mock private VMInstanceDao _vmInstanceDao;
@Mock private UserVmDetailsDao _userVmDetailsDao;
@Mock private NetworkDao _networkDao;
@Mock private NetworkServiceMapDao _ntwkSrvcDao;
@Mock private DataCenterVO dataCenterVO;
@Mock private DataStore dataStore;
@Mock private DeployDestination deployDestination;
@Mock private EndPoint endpoint;
@Mock private EndPointSelector _ep;
@Mock private GuestOSCategoryVO guestOSCategoryVo;
@Mock private GuestOSVO guestOSVO;
@Mock private HostVO hostVO;
@Mock private NetworkVO network;
@Mock private Nic nic;
@Mock private NicProfile nicp;
@Mock private ServiceOfferingVO serviceOfferingVO;
@Mock private UserVmVO virtualMachine;
@InjectMocks private final ConfigDriveNetworkElement _configDrivesNetworkElement = new ConfigDriveNetworkElement();
@InjectMocks @Spy private NetworkModelImpl _networkModel = new NetworkModelImpl();
@org.junit.Before
public void setUp() throws NoSuchFieldException, IllegalAccessException {
MockitoAnnotations.initMocks(this);
_configDrivesNetworkElement._networkModel = _networkModel;
when(_dataStoreMgr.getImageStore(DATACENTERID)).thenReturn(dataStore);
when(_ep.select(dataStore)).thenReturn(endpoint);
when(_vmDao.findById(VMID)).thenReturn(virtualMachine);
when(_dcDao.findById(DATACENTERID)).thenReturn(dataCenterVO);
when(_hostDao.findById(HOSTID)).thenReturn(hostVO);
doReturn(nic).when(_networkModel).getDefaultNic(VMID);
when(_serviceOfferingDao.findByIdIncludingRemoved(VMID, SOID)).thenReturn(serviceOfferingVO);
when(_guestOSDao.findById(anyLong())).thenReturn(guestOSVO);
when(_guestOSCategoryDao.findById(anyLong())).thenReturn(guestOSCategoryVo);
when(_configDao.getValue("cloud.identifier")).thenReturn(CLOUD_ID);
when(network.getDataCenterId()).thenReturn(DATACENTERID);
when(guestOSCategoryVo.getName()).thenReturn("Linux");
when(dataCenterVO.getName()).thenReturn(ZONENAME);
when(serviceOfferingVO.getDisplayText()).thenReturn(VMOFFERING);
when(guestOSVO.getCategoryId()).thenReturn(0L);
when(virtualMachine.getGuestOSId()).thenReturn(0L);
when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User);
when(virtualMachine.getId()).thenReturn(VMID);
when(virtualMachine.getServiceOfferingId()).thenReturn(SOID);
when(virtualMachine.getDataCenterId()).thenReturn(DATACENTERID);
when(virtualMachine.getInstanceName()).thenReturn(VMINSTANCENAME);
when(virtualMachine.getUserData()).thenReturn(Base64.encode(VMUSERDATA.getBytes()));
when(deployDestination.getHost()).thenReturn(hostVO);
when(hostVO.getId()).thenReturn(HOSTID);
when(nic.isDefaultNic()).thenReturn(true);
when(nic.getNetworkId()).thenReturn(NETWORK_ID);
when(network.getId()).thenReturn(NETWORK_ID);
when(_networkModel.getNetwork(NETWORK_ID)).thenReturn(network);
//when(_networkModel.getUserDataUpdateProvider(network)).thenReturn(_configDrivesNetworkElement);
when(_ntwkSrvcDao.getProviderForServiceInNetwork(NETWORK_ID, Network.Service.UserData)).thenReturn(_configDrivesNetworkElement.getProvider().getName());
_networkModel.setNetworkElements(Arrays.asList(_configDrivesNetworkElement));
_networkModel.start();
}
@Test
public void testCanHandle() throws InsufficientCapacityException, ResourceUnavailableException {
final NetworkOfferingVO ntwkoffer = mock(NetworkOfferingVO.class);
when(ntwkoffer.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
assertTrue(_configDrivesNetworkElement.implement(null, ntwkoffer, null,null));
when(ntwkoffer.getTrafficType()).thenReturn(Networks.TrafficType.Public);
assertFalse(_configDrivesNetworkElement.implement(null, ntwkoffer, null, null));
}
@Test
@SuppressWarnings("unchecked")
public void testExpunge() throws NoTransitionException, NoSuchFieldException, IllegalAccessException {
final StateMachine2<VirtualMachine.State, VirtualMachine.Event, VirtualMachine> stateMachine = VirtualMachine.State.getStateMachine();
final Field listenersField = StateMachine2.class.getDeclaredField("_listeners");
listenersField.setAccessible(true);
List<StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualMachine>> listeners =
(List<StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualMachine>>)listenersField.get(stateMachine);
listeners.clear();
_configDrivesNetworkElement.start();
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
when(_vmInstanceDao.updateState(VirtualMachine.State.Stopped, VirtualMachine.Event.ExpungeOperation, VirtualMachine.State.Expunging, virtualMachine, null)).thenReturn(true);
final Answer answer = mock(Answer.class);
when(endpoint.sendMessage(any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
when(answer.getResult()).thenReturn(true);
stateMachine.transitTo(virtualMachine, VirtualMachine.Event.ExpungeOperation, null, _vmInstanceDao);
ArgumentCaptor<HandleConfigDriveIsoCommand> commandCaptor = ArgumentCaptor.forClass(HandleConfigDriveIsoCommand.class);
verify(endpoint, times(1)).sendMessage(commandCaptor.capture());
HandleConfigDriveIsoCommand deleteCommand = commandCaptor.getValue();
assertThat(deleteCommand.isCreate(), is(false));
assertThat(deleteCommand.isUpdate(), is(false));
}
@Test
public void testRelease() {
final Answer answer = mock(Answer.class);
when(endpoint.sendMessage(any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
when(answer.getResult()).thenReturn(true);
VirtualMachineProfile profile = new VirtualMachineProfileImpl(virtualMachine, null, serviceOfferingVO, null, null);
assertTrue(_configDrivesNetworkElement.release(network, nicp, profile, null));
}
@Test
public void testGetCapabilities () {
assertThat(_configDrivesNetworkElement.getCapabilities(), hasEntry(Network.Service.UserData, null));
}
@Test
public void testAddPasswordAndUserdata() throws InsufficientCapacityException, ResourceUnavailableException {
final Answer answer = mock(Answer.class);
final UserVmDetailVO userVmDetailVO = mock(UserVmDetailVO.class);
when(endpoint.sendMessage(any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
when(answer.getResult()).thenReturn(true);
when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
when(userVmDetailVO.getValue()).thenReturn(PUBLIC_KEY);
when(_userVmDetailsDao.findDetail(anyLong(), anyString())).thenReturn(userVmDetailVO);
Map<VirtualMachineProfile.Param, Object> parms = Maps.newHashMap();
parms.put(VirtualMachineProfile.Param.VmPassword, PASSWORD);
parms.put(VirtualMachineProfile.Param.VmSshPubKey, PUBLIC_KEY);
VirtualMachineProfile profile = new VirtualMachineProfileImpl(virtualMachine, null, serviceOfferingVO, null, parms);
assertTrue(_configDrivesNetworkElement.addPasswordAndUserdata(
network, nicp, profile, deployDestination, null));
ArgumentCaptor<HandleConfigDriveIsoCommand> commandCaptor = ArgumentCaptor.forClass(HandleConfigDriveIsoCommand.class);
verify(endpoint, times(1)).sendMessage(commandCaptor.capture());
HandleConfigDriveIsoCommand result = commandCaptor.getValue();
List<String[]> actualVmData = result.getVmData();
assertThat(actualVmData, containsInAnyOrder(
new String[]{"userdata", "user_data", VMUSERDATA},
new String[]{"metadata", "service-offering", VMOFFERING},
new String[]{"metadata", "availability-zone", ZONENAME},
new String[]{"metadata", "local-hostname", VMINSTANCENAME},
new String[]{"metadata", "vm-id", String.valueOf(VMID)},
new String[]{"metadata", "instance-id", String.valueOf(VMINSTANCENAME)},
new String[]{"metadata", "public-keys", PUBLIC_KEY},
new String[]{"metadata", "cloud-identifier", String.format("CloudStack-{%s}", CLOUD_ID)},
new String[]{PASSWORD, "vm_password", PASSWORD}
));
}
}

View File

@ -17,8 +17,9 @@
package com.cloud.vm; package com.cloud.vm;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean;
@ -35,6 +36,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -45,13 +47,7 @@ import java.util.UUID;
import com.cloud.dc.VlanVO; import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.VlanDao; import com.cloud.dc.dao.VlanDao;
import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.element.UserDataServiceProvider;
import com.cloud.storage.Storage;
import com.cloud.user.User;
import com.cloud.event.dao.UsageEventDao;
import com.cloud.uservm.UserVm;
import org.junit.Assert; import org.junit.Assert;
import org.apache.cloudstack.api.BaseCmd;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
@ -61,6 +57,7 @@ import org.mockito.Spy;
import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd; import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
@ -72,14 +69,16 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import com.cloud.capacity.CapacityManager; import com.cloud.capacity.CapacityManager;
import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ConfigurationManager;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeployDestination;
import com.cloud.event.dao.UsageEventDao;
import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
@ -96,11 +95,13 @@ import com.cloud.network.NetworkModel;
import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.NetworkVO;
import com.cloud.network.element.UserDataServiceProvider;
import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering;
import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
@ -112,9 +113,11 @@ import com.cloud.user.AccountManager;
import com.cloud.user.AccountService; import com.cloud.user.AccountService;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
import com.cloud.user.ResourceLimitService; import com.cloud.user.ResourceLimitService;
import com.cloud.user.User;
import com.cloud.user.UserVO; import com.cloud.user.UserVO;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.EntityManager;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachine.State;
@ -123,7 +126,6 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao; import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
public class UserVmManagerTest { public class UserVmManagerTest {
@ -1084,4 +1086,14 @@ public class UserVmManagerTest {
assertTrue("validate return the value with padding", encodedUserdataWithPadding.equals(_userVmMgr.validateUserData(encodedUserdataWithPadding, BaseCmd.HTTPMethod.GET))); assertTrue("validate return the value with padding", encodedUserdataWithPadding.equals(_userVmMgr.validateUserData(encodedUserdataWithPadding, BaseCmd.HTTPMethod.GET)));
} }
@Test
public void testValidateUrlEncodedBase64() throws UnsupportedEncodingException {
// fo should be encoded in base64 either as Zm8 or Zm8=
String encodedUserdata = "Zm+8/w8=";
String urlEncodedUserdata = java.net.URLEncoder.encode(encodedUserdata, "UTF-8");
// Verify that we accept both but return the padded version
assertEquals("validate return the value with padding", encodedUserdata, _userVmMgr.validateUserData(encodedUserdata, BaseCmd.HTTPMethod.GET));
assertEquals("validate return the value with padding", encodedUserdata, _userVmMgr.validateUserData(urlEncodedUserdata, BaseCmd.HTTPMethod.GET));
}
} }

View File

@ -16,6 +16,21 @@
// under the License. // under the License.
package org.apache.cloudstack.storage.resource; package org.apache.cloudstack.storage.resource;
import static com.cloud.network.NetworkModel.CONFIGDATA_CONTENT;
import static com.cloud.network.NetworkModel.CONFIGDATA_DIR;
import static com.cloud.network.NetworkModel.CONFIGDATA_FILE;
import static com.cloud.network.NetworkModel.METATDATA_DIR;
import static com.cloud.network.NetworkModel.PASSWORD_DIR;
import static com.cloud.network.NetworkModel.PASSWORD_FILE;
import static com.cloud.network.NetworkModel.PUBLIC_KEYS_FILE;
import static com.cloud.network.NetworkModel.USERDATA_DIR;
import static com.cloud.network.NetworkModel.USERDATA_FILE;
import static com.cloud.utils.StringUtils.join;
import static com.cloud.utils.storage.S3.S3Utils.putFile;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.apache.commons.lang.StringUtils.substringAfterLast;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
@ -29,6 +44,8 @@ import java.io.UnsupportedEncodingException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.URI; import java.net.URI;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -36,7 +53,68 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import com.amazonaws.services.s3.model.S3ObjectSummary; import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.cloudstack.framework.security.keystore.KeystoreManager;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.command.DownloadProgressCommand;
import org.apache.cloudstack.storage.command.TemplateOrVolumePostUploadCommand;
import org.apache.cloudstack.storage.command.UploadStatusAnswer;
import org.apache.cloudstack.storage.command.UploadStatusAnswer.UploadStatus;
import org.apache.cloudstack.storage.command.UploadStatusCommand;
import org.apache.cloudstack.storage.template.DownloadManager;
import org.apache.cloudstack.storage.template.DownloadManagerImpl;
import org.apache.cloudstack.storage.template.DownloadManagerImpl.ZfsPathParser;
import org.apache.cloudstack.storage.template.UploadEntity;
import org.apache.cloudstack.storage.template.UploadManager;
import org.apache.cloudstack.storage.template.UploadManagerImpl;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.cloudstack.utils.security.DigestHelper;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckHealthAnswer; import com.cloud.agent.api.CheckHealthAnswer;
import com.cloud.agent.api.CheckHealthCommand; import com.cloud.agent.api.CheckHealthCommand;
@ -45,6 +123,7 @@ import com.cloud.agent.api.ComputeChecksumCommand;
import com.cloud.agent.api.DeleteSnapshotsDirCommand; import com.cloud.agent.api.DeleteSnapshotsDirCommand;
import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsAnswer;
import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.GetStorageStatsCommand;
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingCommand;
import com.cloud.agent.api.PingStorageCommand; import com.cloud.agent.api.PingStorageCommand;
import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.ReadyAnswer;
@ -82,6 +161,7 @@ import com.cloud.host.Host;
import com.cloud.host.Host.Type; import com.cloud.host.Host.Type;
import com.cloud.configuration.Resource; import com.cloud.configuration.Resource;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.NetworkModel;
import com.cloud.resource.ServerResourceBase; import com.cloud.resource.ServerResourceBase;
import com.cloud.storage.DataStoreRole; import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
@ -109,63 +189,7 @@ import com.cloud.utils.script.OutputInterpreter;
import com.cloud.utils.script.Script; import com.cloud.utils.script.Script;
import com.cloud.utils.storage.S3.S3Utils; import com.cloud.utils.storage.S3.S3Utils;
import com.cloud.vm.SecondaryStorageVm; import com.cloud.vm.SecondaryStorageVm;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.apache.cloudstack.framework.security.keystore.KeystoreManager;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.command.DownloadProgressCommand;
import org.apache.cloudstack.storage.command.TemplateOrVolumePostUploadCommand;
import org.apache.cloudstack.storage.command.UploadStatusAnswer;
import org.apache.cloudstack.storage.command.UploadStatusAnswer.UploadStatus;
import org.apache.cloudstack.storage.command.UploadStatusCommand;
import org.apache.cloudstack.storage.template.DownloadManager;
import org.apache.cloudstack.storage.template.DownloadManagerImpl;
import org.apache.cloudstack.storage.template.DownloadManagerImpl.ZfsPathParser;
import org.apache.cloudstack.storage.template.UploadEntity;
import org.apache.cloudstack.storage.template.UploadManager;
import org.apache.cloudstack.storage.template.UploadManagerImpl;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import static com.cloud.utils.StringUtils.join;
import static com.cloud.utils.storage.S3.S3Utils.putFile;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.apache.commons.lang.StringUtils.substringAfterLast;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
@ -176,6 +200,16 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
private static final String TEMPLATE_ROOT_DIR = "template/tmpl"; private static final String TEMPLATE_ROOT_DIR = "template/tmpl";
private static final String VOLUME_ROOT_DIR = "volumes"; private static final String VOLUME_ROOT_DIR = "volumes";
private static final String POST_UPLOAD_KEY_LOCATION = "/etc/cloudstack/agent/ms-psk"; private static final String POST_UPLOAD_KEY_LOCATION = "/etc/cloudstack/agent/ms-psk";
private static final String cloudStackConfigDriveName = "/cloudstack/";
private static final String openStackConfigDriveName = "/openstack/latest/";
private static final Map<String, String> updatableConfigData = Maps.newHashMap();
static {
updatableConfigData.put(PUBLIC_KEYS_FILE, METATDATA_DIR);
updatableConfigData.put(USERDATA_FILE, USERDATA_DIR);
updatableConfigData.put(PASSWORD_FILE, PASSWORD_DIR);
}
int _timeout; int _timeout;
@ -291,6 +325,8 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
return execute((DeleteCommand)cmd); return execute((DeleteCommand)cmd);
} else if (cmd instanceof UploadStatusCommand) { } else if (cmd instanceof UploadStatusCommand) {
return execute((UploadStatusCommand)cmd); return execute((UploadStatusCommand)cmd);
} else if (cmd instanceof HandleConfigDriveIsoCommand) {
return execute((HandleConfigDriveIsoCommand)cmd);
} else if (cmd instanceof GetDatadisksCommand) { } else if (cmd instanceof GetDatadisksCommand) {
return execute((GetDatadisksCommand)cmd); return execute((GetDatadisksCommand)cmd);
} else if (cmd instanceof CreateDatadiskTemplateCommand) { } else if (cmd instanceof CreateDatadiskTemplateCommand) {
@ -300,6 +336,306 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
} }
} }
private Answer execute(HandleConfigDriveIsoCommand cmd) {
if (cmd.isCreate()) {
s_logger.debug(String.format("VMdata %s, attach = %s", cmd.getVmData(), cmd.isCreate()));
if(cmd.getVmData() == null) return new Answer(cmd, false, "No Vmdata available");
String nfsMountPoint = getRootDir(cmd.getDestStore().getUrl(), _nfsVersion);
File isoFile = new File(nfsMountPoint, cmd.getIsoFile());
if(isoFile.exists()) {
if (!cmd.isUpdate()) {
return new Answer(cmd, true, "ISO already available");
} else {
// Find out if we have to recover the password/ssh-key from the already available ISO.
try {
List<String[]> recoveredVmData = recoverVmData(isoFile);
for (String[] vmDataEntry : cmd.getVmData()) {
if (updatableConfigData.containsKey(vmDataEntry[CONFIGDATA_FILE])
&& updatableConfigData.get(vmDataEntry[CONFIGDATA_FILE]).equals(vmDataEntry[CONFIGDATA_DIR])) {
updateVmData(recoveredVmData, vmDataEntry);
}
}
cmd.setVmData(recoveredVmData);
} catch (IOException e) {
return new Answer(cmd, e);
}
}
}
return createConfigDriveIsoForVM(cmd);
} else {
DataStoreTO dstore = cmd.getDestStore();
if (dstore instanceof NfsTO) {
NfsTO nfs = (NfsTO) dstore;
String relativeTemplatePath = new File(cmd.getIsoFile()).getParent();
String nfsMountPoint = getRootDir(nfs.getUrl(), _nfsVersion);
File tmpltPath = new File(nfsMountPoint, relativeTemplatePath);
try {
FileUtils.deleteDirectory(tmpltPath);
} catch (IOException e) {
return new Answer(cmd, e);
}
return new Answer(cmd);
} else {
return new Answer(cmd, false, "Not implemented yet");
}
}
}
private void updateVmData(List<String[]> recoveredVmData, String[] vmDataEntry) {
for (String[] recoveredEntry : recoveredVmData) {
if (recoveredEntry[CONFIGDATA_DIR].equals(vmDataEntry[CONFIGDATA_DIR])
&& recoveredEntry[CONFIGDATA_FILE].equals(vmDataEntry[CONFIGDATA_FILE])) {
recoveredEntry[CONFIGDATA_CONTENT] = vmDataEntry[CONFIGDATA_CONTENT];
return;
}
}
recoveredVmData.add(vmDataEntry);
}
private List<String[]> recoverVmData(File isoFile) throws IOException {
String tempDirName = null;
List<String[]> recoveredVmData = Lists.newArrayList();
boolean mounted = false;
try {
Path tempDir = java.nio.file.Files.createTempDirectory("ConfigDrive");
tempDirName = tempDir.toString();
// Unpack the current config drive file
Script command = new Script(!_inSystemVM, "mount", _timeout, s_logger);
command.add("-o", "loop");
command.add(isoFile.getAbsolutePath());
command.add(tempDirName);
String result = command.execute();
if (result != null) {
String errMsg = "Unable to mount " + isoFile.getAbsolutePath() + " at " + tempDirName + " due to " + result;
s_logger.error(errMsg);
throw new IOException(errMsg);
}
mounted = true;
// Scan directory structure
for (File configDirectory: (new File(tempDirName, "cloudstack")).listFiles()){
for (File configFile: configDirectory.listFiles()) {
recoveredVmData.add(new String[]{configDirectory.getName(),
Files.getNameWithoutExtension(configFile.getName()),
Files.readFirstLine(configFile, Charset.defaultCharset())});
}
}
} finally {
if (mounted) {
Script command = new Script(!_inSystemVM, "umount", _timeout, s_logger);
command.add(tempDirName);
String result = command.execute();
if (result != null) {
s_logger.warn("Unable to umount " + tempDirName + " due to " + result);
}
}
try {
FileUtils.deleteDirectory(new File(tempDirName));
} catch (IOException ioe) {
s_logger.warn("Failed to delete ConfigDrive temporary directory: " + tempDirName, ioe);
}
}
return recoveredVmData;
}
public Answer createConfigDriveIsoForVM(HandleConfigDriveIsoCommand cmd) {
//create folder for the VM
if (cmd.getVmData() != null) {
Path tempDir = null;
String tempDirName = null;
try {
tempDir = java.nio.file.Files.createTempDirectory("ConfigDrive");
tempDirName = tempDir.toString();
//create OpenStack files
//create folder with empty files
File openStackFolder = new File(tempDirName + openStackConfigDriveName);
if (openStackFolder.exists() || openStackFolder.mkdirs()) {
File vendorDataFile = new File(openStackFolder,"vendor_data.json");
try (FileWriter fw = new FileWriter(vendorDataFile); BufferedWriter bw = new BufferedWriter(fw)) {
bw.write("{}");
} catch (IOException ex) {
s_logger.error("Failed to create file ", ex);
return new Answer(cmd, ex);
}
File networkDataFile = new File(openStackFolder, "network_data.json");
try (FileWriter fw = new FileWriter(networkDataFile); BufferedWriter bw = new BufferedWriter(fw)) {
bw.write("{}");
} catch (IOException ex) {
s_logger.error("Failed to create file ", ex);
return new Answer(cmd, ex);
}
} else {
s_logger.error("Failed to create folder " + openStackFolder);
return new Answer(cmd, false, "Failed to create folder " + openStackFolder);
}
JsonObject metaData = new JsonObject();
for (String[] item : cmd.getVmData()) {
String dataType = item[CONFIGDATA_DIR];
String fileName = item[CONFIGDATA_FILE];
String content = item[CONFIGDATA_CONTENT];
s_logger.debug(String.format("[createConfigDriveIsoForVM] dataType=%s, filename=%s, content=%s",
dataType, fileName, (fileName.equals(PASSWORD_FILE)?"********":content)));
// create file with content in folder
if (dataType != null && !dataType.isEmpty()) {
//create folder
File typeFolder = new File(tempDirName + cloudStackConfigDriveName + dataType);
if (typeFolder.exists() || typeFolder.mkdirs()) {
if (StringUtils.isNotEmpty(content)) {
File file = new File(typeFolder, fileName + ".txt");
try (FileWriter fw = new FileWriter(file); BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(content);
} catch (IOException ex) {
s_logger.error("Failed to create file ", ex);
return new Answer(cmd, ex);
}
}
} else {
s_logger.error("Failed to create folder " + typeFolder);
return new Answer(cmd, false, "Failed to create folder " + typeFolder);
}
//now write the file to the OpenStack directory
metaData = constructOpenStackMetaData(metaData, dataType, fileName, content);
}
}
File metaDataFile = new File(openStackFolder, "meta_data.json");
try (FileWriter fw = new FileWriter(metaDataFile); BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(metaData.toString());
} catch (IOException ex) {
s_logger.error("Failed to create file ", ex);
return new Answer(cmd, ex);
}
String linkResult = linkUserData(tempDirName);
if (linkResult != null) {
String errMsg = "Unable to create user_data link due to " + linkResult;
s_logger.warn(errMsg);
return new Answer(cmd, false, errMsg);
}
File tmpIsoStore = new File(tempDirName, new File(cmd.getIsoFile()).getName());
Script command = new Script(!_inSystemVM, "/usr/bin/genisoimage", _timeout, s_logger);
command.add("-o", tmpIsoStore.getAbsolutePath());
command.add("-ldots");
command.add("-allow-lowercase");
command.add("-allow-multidot");
command.add("-cache-inodes"); // Enable caching inode and device numbers to find hard links to files.
command.add("-l");
command.add("-quiet");
command.add("-J");
command.add("-r");
command.add("-V", cmd.getConfigDriveLabel());
command.add(tempDirName);
s_logger.debug("execute command: " + command.toString());
String result = command.execute();
if (result != null) {
String errMsg = "Unable to create iso file: " + cmd.getIsoFile() + " due to " + result;
s_logger.warn(errMsg);
return new Answer(cmd, false, errMsg);
}
copyLocalToNfs(tmpIsoStore, new File(cmd.getIsoFile()), cmd.getDestStore());
} catch (IOException e) {
return new Answer(cmd, e);
} catch (ConfigurationException e) {
s_logger.warn("SecondStorageException ", e);
return new Answer(cmd, e);
} finally {
try {
FileUtils.deleteDirectory(tempDir.toFile());
} catch (IOException ioe) {
s_logger.warn("Failed to delete ConfigDrive temporary directory: " + tempDirName, ioe);
}
}
}
return new Answer(cmd);
}
JsonObject constructOpenStackMetaData(JsonObject metaData, String dataType, String fileName, String content) {
if (dataType.equals(NetworkModel.METATDATA_DIR) && StringUtils.isNotEmpty(content)) {
//keys are a special case in OpenStack format
if (NetworkModel.PUBLIC_KEYS_FILE.equals(fileName)) {
String[] keyArray = content.replace("\\n", "").split(" ");
String keyName = "key";
if (keyArray.length > 3 && StringUtils.isNotEmpty(keyArray[2])){
keyName = keyArray[2];
}
JsonObject keyLegacy = new JsonObject();
keyLegacy.addProperty("type", "ssh");
keyLegacy.addProperty("data", content.replace("\\n", ""));
keyLegacy.addProperty("name", keyName);
metaData.add("keys", arrayOf(keyLegacy));
JsonObject key = new JsonObject();
key.addProperty(keyName, content);
metaData.add("public_keys", key);
} else if (NetworkModel.openStackFileMapping.get(fileName) != null) {
metaData.addProperty(NetworkModel.openStackFileMapping.get(fileName), content);
}
}
return metaData;
}
private static JsonArray arrayOf(JsonElement... elements) {
JsonArray array = new JsonArray();
for (JsonElement element : elements) {
array.add(element);
}
return array;
}
private String linkUserData(String tempDirName) {
//Hard link the user_data.txt file with the user_data file in the OpenStack directory.
String userDataFilePath = tempDirName + cloudStackConfigDriveName + "userdata/user_data.txt";
if ((new File(userDataFilePath).exists())) {
Script hardLink = new Script(!_inSystemVM, "ln", _timeout, s_logger);
hardLink.add(userDataFilePath);
hardLink.add(tempDirName + openStackConfigDriveName + "user_data");
s_logger.debug("execute command: " + hardLink.toString());
return hardLink.execute();
}
return null;
}
protected void copyLocalToNfs(File localFile, File isoFile, DataStoreTO destData) throws ConfigurationException, IOException {
String scriptsDir = "scripts/storage/secondary";
String createVolScr = Script.findScript(scriptsDir, "createvolume.sh");
if (createVolScr == null) {
throw new ConfigurationException("Unable to find createvolume.sh");
}
s_logger.info("createvolume.sh found in " + createVolScr);
int installTimeoutPerGig = 180 * 60 * 1000;
int imgSizeGigs = (int) Math.ceil(localFile.length() * 1.0d / (1024 * 1024 * 1024));
imgSizeGigs++; // add one just in case
long timeout = imgSizeGigs * installTimeoutPerGig;
Script scr = new Script(createVolScr, timeout, s_logger);
scr.add("-s", Integer.toString(imgSizeGigs));
scr.add("-n", isoFile.getName());
scr.add("-t", getRootDir(destData.getUrl(), _nfsVersion) + "/" + isoFile.getParent());
scr.add("-f", localFile.getAbsolutePath());
scr.add("-d", "configDrive");
String result;
result = scr.execute();
if (result != null) {
// script execution failure
throw new CloudRuntimeException("Failed to run script " + createVolScr);
}
}
public Answer execute(GetDatadisksCommand cmd) { public Answer execute(GetDatadisksCommand cmd) {
DataTO srcData = cmd.getData(); DataTO srcData = cmd.getData();
TemplateObjectTO template = (TemplateObjectTO)srcData; TemplateObjectTO template = (TemplateObjectTO)srcData;
@ -752,7 +1088,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
FormatInfo info = processor.process(destPath, null, templateUuid); FormatInfo info = processor.process(destPath, null, templateUuid);
TemplateLocation loc = new TemplateLocation(_storage, destPath); TemplateLocation loc = new TemplateLocation(_storage, destPath);
loc.create(destData.getId(), true, templateUuid); loc.create(1, true, templateUuid);
loc.addFormat(info); loc.addFormat(info);
loc.save(); loc.save();
TemplateProp prop = loc.getTemplateInfo(); TemplateProp prop = loc.getTemplateInfo();
@ -836,7 +1172,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
FormatInfo info = processor.process(destPath, null, templateName); FormatInfo info = processor.process(destPath, null, templateName);
TemplateLocation loc = new TemplateLocation(_storage, destPath); TemplateLocation loc = new TemplateLocation(_storage, destPath);
loc.create(destData.getId(), true, destData.getName()); loc.create(1, true, destData.getName());
loc.addFormat(info); loc.addFormat(info);
loc.save(); loc.save();
@ -2485,10 +2821,9 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
if (_inSystemVM) { if (_inSystemVM) {
_localgw = (String)params.get("localgw"); _localgw = (String)params.get("localgw");
if (_localgw != null) { // can only happen inside service vm if (_localgw != null) { // can only happen inside service vm
String mgmtHosts = (String)params.get("host"); String mgmtHost = (String)params.get("host");
for (final String mgmtHost : mgmtHosts.split(",")) {
addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, mgmtHost); addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, mgmtHost);
}
String internalDns1 = (String)params.get("internaldns1"); String internalDns1 = (String)params.get("internaldns1");
if (internalDns1 == null) { if (internalDns1 == null) {
s_logger.warn("No DNS entry found during configuration of NfsSecondaryStorage"); s_logger.warn("No DNS entry found during configuration of NfsSecondaryStorage");

View File

@ -31,7 +31,7 @@ public_key=$filepath/metadata/public_keys.txt
vm_password=$filepath/password/vm_password.txt vm_password=$filepath/password/vm_password.txt
# If lable name is other than config, please change the below line as required # If lable name is other than config, please change the below line as required
DefaultDisk=/dev/disk/by-label/config DefaultDisk=/dev/disk/by-label/config-2
function usage function usage
{ {

View File

@ -29,7 +29,7 @@ user=root
mountdir=$(mktemp -d) mountdir=$(mktemp -d)
# If lable name is other than config, please change the below line as required # If lable name is other than config, please change the below line as required
DefaultDisk=/dev/disk/by-label/config DefaultDisk=/dev/disk/by-label/config-2
Password_File=$mountdir/cloudstack/password/vm_password.txt Password_File=$mountdir/cloudstack/password/vm_password.txt

View File

@ -29,7 +29,7 @@ user=root
mountdir=$(mktemp -d) mountdir=$(mktemp -d)
# If lable name is other than config, please change the below line as required # If lable name is other than config, please change the below line as required
DefaultDisk=/dev/disk/by-label/config DefaultDisk=/dev/disk/by-label/config-2
SSHKey_File=$mountdir/cloudstack/metadata/public_keys.txt SSHKey_File=$mountdir/cloudstack/metadata/public_keys.txt
keys_received=0 keys_received=0

View File

@ -25,7 +25,7 @@ echo Const HKEY_CURRENT_USER = ^&H80000001
echo registryKeyPath = "SOFTWARE\CLOUDSTACKmd5Checksum\" >> %PasswordReset_Script% echo registryKeyPath = "SOFTWARE\CLOUDSTACKmd5Checksum\" >> %PasswordReset_Script%
echo registryKeyName = "PasswordMd5Checksum" >> %PasswordReset_Script% echo registryKeyName = "PasswordMd5Checksum" >> %PasswordReset_Script%
echo CDPath = "" >> %PasswordReset_Script% echo CDPath = "" >> %PasswordReset_Script%
echo configLabelName = "config" >> %PasswordReset_Script% echo configLabelName = "config-2" >> %PasswordReset_Script%
echo. >> %PasswordReset_Script% echo. >> %PasswordReset_Script%
echo. >> %PasswordReset_Script% echo. >> %PasswordReset_Script%
echo. >> %PasswordReset_Script% echo. >> %PasswordReset_Script%

File diff suppressed because it is too large Load Diff

View File

@ -198,7 +198,7 @@ class TestDeployVMs(cloudstackTestCase):
Returns mount path if config drive is attached else False Returns mount path if config drive is attached else False
""" """
mountdir = "/root/iso" mountdir = "/root/iso"
cmd = "blkid -t LABEL='config' /dev/hd? /dev/sd? /dev/xvd? -o device" cmd = "blkid -t LABEL='config-2' /dev/sr? /dev/hd? /dev/sd? /dev/xvd? -o device"
try: try:
self.debug("SSH into VM: %s" % vm_ip) self.debug("SSH into VM: %s" % vm_ip)
ssh = self.vm.get_ssh_client(ipaddress=vm_ip, reconnect=True) ssh = self.vm.get_ssh_client(ipaddress=vm_ip, reconnect=True)
@ -261,7 +261,7 @@ class TestDeployVMs(cloudstackTestCase):
def verifySshKey(self, vm_ip, iso_path): def verifySshKey(self, vm_ip, iso_path):
publicKey_file = iso_path+"/cloudstack/metadata/public_keys.txt" publicKey_file = iso_path+"/cloudstack/metadata/public-keys.txt"
try: try:
self.debug("SSH into VM: %s" % vm_ip) self.debug("SSH into VM: %s" % vm_ip)
ssh = self.vm.get_ssh_client(ipaddress=vm_ip) ssh = self.vm.get_ssh_client(ipaddress=vm_ip)
@ -285,7 +285,7 @@ class TestDeployVMs(cloudstackTestCase):
response = {} response = {}
self.debug("SSH into VM: %s" % vm_ip) self.debug("SSH into VM: %s" % vm_ip)
ssh = self.vm.get_ssh_client(ipaddress=vm_ip, reconnect=True) ssh = self.vm.get_ssh_client(ipaddress=vm_ip, reconnect=True)
vm_files = ["availability_zone.txt", "instance_id.txt", "service_offering.txt", "vm_id.txt"] vm_files = ["availability-zone.txt", "instance-id.txt", "service-offering.txt", "vm-id.txt"]
for file in vm_files: for file in vm_files:
cmd = "cat %s" % metadata_dir+file cmd = "cat %s" % metadata_dir+file
res = ssh.execute(cmd) res = ssh.execute(cmd)
@ -296,22 +296,22 @@ class TestDeployVMs(cloudstackTestCase):
def verifyMetaData(self, metadata): def verifyMetaData(self, metadata):
metadata_files = ["availability_zone.txt", "instance_id.txt", "service_offering.txt", "vm_id.txt"] metadata_files = ["availability-zone.txt", "instance-id.txt", "service-offering.txt", "vm-id.txt"]
for mfile in metadata_files: for mfile in metadata_files:
if mfile not in metadata: if mfile not in metadata:
self.fail("{} file is not found in vm metadata".format(mfile)) self.fail("{} file is not found in vm metadata".format(mfile))
self.assertEqual( self.assertEqual(
str(metadata["availability_zone.txt"][0]), str(metadata["availability-zone.txt"][0]),
self.zone.name, self.zone.name,
"Zone name inside metadata does not match with the zone" "Zone name inside metadata does not match with the zone"
) )
self.assertEqual( self.assertEqual(
str(metadata["instance_id.txt"][0]), str(metadata["instance-id.txt"][0]),
self.vm.instancename, self.vm.instancename,
"vm name inside metadata does not match with the instance name" "vm name inside metadata does not match with the instance name"
) )
self.assertEqual( self.assertEqual(
str(metadata["service_offering.txt"][0]), str(metadata["service-offering.txt"][0]),
self.vm.serviceofferingname, self.vm.serviceofferingname,
"Service offering inside metadata does not match with the instance offering" "Service offering inside metadata does not match with the instance offering"
) )
@ -320,7 +320,7 @@ class TestDeployVMs(cloudstackTestCase):
self.vm.instancename) self.vm.instancename)
self.assertEqual(validateList(qresultset)[0], PASS, "sql query returned invalid response") self.assertEqual(validateList(qresultset)[0], PASS, "sql query returned invalid response")
self.assertEqual( self.assertEqual(
metadata["vm_id.txt"][0], metadata["vm-id.txt"][0],
unicode(qresultset[0][0]), unicode(qresultset[0][0]),
"vm id in metadata does not match with the vm id from cloud db" "vm id in metadata does not match with the vm id from cloud db"
) )

View File

@ -44,6 +44,8 @@ from marvin.lib.common import (get_domain,
get_template, get_template,
get_zone) get_zone)
from marvin.cloudstackAPI import (restartVPC, from marvin.cloudstackAPI import (restartVPC,
enableNuageUnderlayVlanIpRange,
disableNuageUnderlayVlanIpRange,
listNuageUnderlayVlanIpRanges) listNuageUnderlayVlanIpRanges)
# Import System Modules # Import System Modules
from retry import retry from retry import retry
@ -320,6 +322,31 @@ class nuageTestCase(cloudstackTestCase):
self.debug("Cleanup complete!") self.debug("Cleanup complete!")
return return
# enable_NuageUnderlayPublicIpRange - Enables/configures underlay
# networking for the given public IP range in Nuage VSP
def enable_NuageUnderlayPublicIpRange(self, vlanid):
cmd = enableNuageUnderlayVlanIpRange. \
enableNuageUnderlayVlanIpRangeCmd()
cmd.id = vlanid
self.api_client.enableNuageUnderlayVlanIpRange(cmd)
# disable_NuageUnderlayPublicIpRange - Disables/de-configures underlay
# networking for the given public IP range in Nuage VSP
def disable_NuageUnderlayPublicIpRange(self, public_ip_range):
cmd = disableNuageUnderlayVlanIpRange. \
disableNuageUnderlayVlanIpRangeCmd()
cmd.id = public_ip_range.vlan.id
self.api_client.enableNuageUnderlayVlanIpRange(cmd)
# list_NuageUnderlayPublicIpRanges - Lists underlay networking
# enabled/configured public IP ranges in Nuage VSP
def list_NuageUnderlayPublicIpRanges(self, public_ip_range=None):
cmd = listNuageUnderlayVlanIpRanges.listNuageUnderlayVlanIpRangesCmd()
if public_ip_range:
cmd.id = public_ip_range.vlan.id
cmd.underlay = True
return self.api_client.listNuageUnderlayVlanIpRanges(cmd)
# create_VpcOffering - Creates VPC offering # create_VpcOffering - Creates VPC offering
@needscleanup @needscleanup
def create_VpcOffering(cls, vpc_offering, suffix=None): def create_VpcOffering(cls, vpc_offering, suffix=None):
@ -395,6 +422,7 @@ class nuageTestCase(cloudstackTestCase):
testdata = cls.test_data["network"] testdata = cls.test_data["network"]
testdata["name"] = "TestNet-" + gateway + "-" + str(nw_off.name) testdata["name"] = "TestNet-" + gateway + "-" + str(nw_off.name)
testdata["displaytext"] = "Test Network" testdata["displaytext"] = "Test Network"
testdata["gateway"] = gateway
testdata["netmask"] = netmask testdata["netmask"] = netmask
network = Network.create(cls.api_client, network = Network.create(cls.api_client,
testdata, testdata,
@ -402,7 +430,6 @@ class nuageTestCase(cloudstackTestCase):
domainid=account.domainid, domainid=account.domainid,
networkofferingid=nw_off.id, networkofferingid=nw_off.id,
zoneid=cls.zone.id, zoneid=cls.zone.id,
gateway=gateway,
vlan=vlan, vlan=vlan,
externalid=externalid, externalid=externalid,
vpcid=vpc.id if vpc else cls.vpc.id vpcid=vpc.id if vpc else cls.vpc.id
@ -435,7 +462,7 @@ class nuageTestCase(cloudstackTestCase):
# create_VM - Creates VM in the given network(s) # create_VM - Creates VM in the given network(s)
@needscleanup @needscleanup
def create_VM(cls, network_list, host_id=None, start_vm=True, def create_VM(cls, network_list, host_id=None, start_vm=True,
testdata=None, account=None): testdata=None, account=None, keypair=None):
network_ids = [] network_ids = []
if isinstance(network_list, list): if isinstance(network_list, list):
for network in network_list: for network in network_list:
@ -457,7 +484,8 @@ class nuageTestCase(cloudstackTestCase):
zoneid=cls.zone.id, zoneid=cls.zone.id,
networkids=network_ids, networkids=network_ids,
startvm=start_vm, startvm=start_vm,
hostid=host_id hostid=host_id,
keypair=keypair
) )
cls.debug("Created VM with ID - %s in network(s) with ID(s) - %s" cls.debug("Created VM with ID - %s in network(s) with ID(s) - %s"
% (vm.id, network_ids)) % (vm.id, network_ids))
@ -490,22 +518,22 @@ class nuageTestCase(cloudstackTestCase):
def migrate_VM(self, vm): def migrate_VM(self, vm):
self.debug("Checking if a host is available for migration...") self.debug("Checking if a host is available for migration...")
hosts = Host.listForMigration(self.api_client, virtualmachineid=vm.id) hosts = Host.listForMigration(self.api_client, virtualmachineid=vm.id)
if hosts:
self.assertEqual(isinstance(hosts, list), True, self.assertEqual(isinstance(hosts, list), True,
"List hosts should return a valid list" "List hosts should return a valid list"
) )
# Remove the host of current VM from the hosts list
vm_info = VirtualMachine.list(self.api_client, id=vm.id)[0]
hosts[:] = [host for host in hosts if host.id != vm_info.hostid]
if len(hosts) <= 0:
self.skipTest("No host available for migration. "
"Test requires at-least 2 hosts")
host = hosts[0] host = hosts[0]
self.debug("Migrating VM with ID: %s to Host: %s" % (vm.id, host.id)) self.debug("Migrating VM with ID: "
"%s to Host: %s" % (vm.id, host.id))
try: try:
vm.migrate(self.api_client, hostid=host.id) vm.migrate(self.api_client, hostid=host.id)
except Exception as e: except Exception as e:
self.fail("Failed to migrate instance, %s" % e) self.fail("Failed to migrate instance, %s" % e)
self.debug("Migrated VM with ID: %s to Host: %s" % (vm.id, host.id)) self.debug("Migrated VM with ID: "
"%s to Host: %s" % (vm.id, host.id))
else:
self.debug("No host available for migration. "
"Test requires at-least 2 hosts")
# delete_VM - Deletes the given VM # delete_VM - Deletes the given VM
def delete_VM(self, vm, expunge=True): def delete_VM(self, vm, expunge=True):

File diff suppressed because it is too large Load Diff

View File

@ -23,9 +23,7 @@ from marvin.lib.base import (Account,
PublicIpRange, PublicIpRange,
Network, Network,
VirtualMachine) VirtualMachine)
from marvin.cloudstackAPI import (enableNuageUnderlayVlanIpRange, from marvin.lib.common import list_virtual_machines
disableNuageUnderlayVlanIpRange,
listNuageUnderlayVlanIpRanges)
from marvin.lib.common import list_virtual_machines from marvin.lib.common import list_virtual_machines
# Import System Modules # Import System Modules
@ -54,31 +52,6 @@ class TestNuageStaticNat(nuageTestCase):
self.cleanup = [self.account] self.cleanup = [self.account]
return return
# enable_NuageUnderlayPublicIpRange - Enables/configures underlay
# networking for the given public IP range in Nuage VSP
def enable_NuageUnderlayPublicIpRange(self, public_ip_range):
cmd = enableNuageUnderlayVlanIpRange.\
enableNuageUnderlayVlanIpRangeCmd()
cmd.id = public_ip_range.vlan.id
self.api_client.enableNuageUnderlayVlanIpRange(cmd)
# disable_NuageUnderlayPublicIpRange - Disables/de-configures underlay
# networking for the given public IP range in Nuage VSP
def disable_NuageUnderlayPublicIpRange(self, public_ip_range):
cmd = disableNuageUnderlayVlanIpRange.\
disableNuageUnderlayVlanIpRangeCmd()
cmd.id = public_ip_range.vlan.id
self.api_client.enableNuageUnderlayVlanIpRange(cmd)
# list_NuageUnderlayPublicIpRanges - Lists underlay networking
# enabled/configured public IP ranges in Nuage VSP
def list_NuageUnderlayPublicIpRanges(self, public_ip_range=None):
cmd = listNuageUnderlayVlanIpRanges.listNuageUnderlayVlanIpRangesCmd()
if public_ip_range:
cmd.id = public_ip_range.vlan.id
cmd.underlay = True
return self.api_client.listNuageUnderlayVlanIpRanges(cmd)
# create_PublicIpRange - Creates public IP range # create_PublicIpRange - Creates public IP range
def create_PublicIpRange(self): def create_PublicIpRange(self):
self.debug("Creating public IP range") self.debug("Creating public IP range")
@ -115,20 +88,21 @@ class TestNuageStaticNat(nuageTestCase):
# validate_NuageUnderlayPublicIpRange - Validates Nuage underlay enabled # validate_NuageUnderlayPublicIpRange - Validates Nuage underlay enabled
# public IP range creation and state # public IP range creation and state
def validate_NuageUnderlayPublicIpRange(self, public_ip_range): def validate_NuageUnderlayPublicIpRange(self, public_ip_range):
nuage_underlay_public_ip_ranges = \ self.nuage_underlay_public_ip_ranges = \
self.list_NuageUnderlayPublicIpRanges(public_ip_range) self.list_NuageUnderlayPublicIpRanges(public_ip_range)
self.assertEqual(isinstance(nuage_underlay_public_ip_ranges, list), self.assertEqual(isinstance(self.nuage_underlay_public_ip_ranges,
list),
True, True,
"List Nuage Underlay Public IP Range should return " "List Nuage Underlay Public IP Range should return "
"a valid list" "a valid list"
) )
self.assertEqual(public_ip_range.vlan.startip, self.assertEqual(public_ip_range.vlan.startip,
nuage_underlay_public_ip_ranges[0].startip, self.nuage_underlay_public_ip_ranges[0].startip,
"Start IP of the public IP range should match with " "Start IP of the public IP range should match with "
"the returned list data" "the returned list data"
) )
self.assertEqual(public_ip_range.vlan.endip, self.assertEqual(public_ip_range.vlan.endip,
nuage_underlay_public_ip_ranges[0].endip, self.nuage_underlay_public_ip_ranges[0].endip,
"End IP of the public IP range should match with the " "End IP of the public IP range should match with the "
"returned list data" "returned list data"
) )
@ -358,7 +332,7 @@ class TestNuageStaticNat(nuageTestCase):
self.debug("Enabling Nuage underlay capability (underlay networking) " self.debug("Enabling Nuage underlay capability (underlay networking) "
"for the created public IP range...") "for the created public IP range...")
self.enable_NuageUnderlayPublicIpRange(public_ip_range) self.enable_NuageUnderlayPublicIpRange(public_ip_range.vlan.id)
self.validate_NuageUnderlayPublicIpRange(public_ip_range) self.validate_NuageUnderlayPublicIpRange(public_ip_range)
self.debug("Nuage underlay capability (underlay networking) for the " self.debug("Nuage underlay capability (underlay networking) for the "
"created public IP range is successfully enabled") "created public IP range is successfully enabled")
@ -532,6 +506,7 @@ class TestNuageStaticNat(nuageTestCase):
self.debug("Acquired public IP in the created Isolated network " self.debug("Acquired public IP in the created Isolated network "
"successfully released in CloudStack") "successfully released in CloudStack")
self.delete_VM(vm_1) self.delete_VM(vm_1)
# Bug CLOUDSTACK-9398 # Bug CLOUDSTACK-9398
""" """
self.debug("Creating a persistent Isolated network with Static NAT " self.debug("Creating a persistent Isolated network with Static NAT "

View File

@ -49,6 +49,27 @@ test_data = {
"forvirtualnetwork": "true", "forvirtualnetwork": "true",
"vlan": "300" "vlan": "300"
}, },
"publiciprange1": {
"gateway": "10.200.100.1",
"netmask": "255.255.255.0",
"startip": "10.200.100.101",
"endip": "10.200.100.105",
"forvirtualnetwork": "false"
},
"publiciprange2": {
"gateway": "10.219.1.1",
"netmask": "255.255.255.0",
"startip": "10.219.1.2",
"endip": "10.219.1.5",
"forvirtualnetwork": "false"
},
"publiciprange3": {
"gateway": "10.200.100.1",
"netmask": "255.255.255.0",
"startip": "10.200.100.2",
"endip": "10.200.100.20",
"forvirtualnetwork": "false"
},
"private_gateway": { "private_gateway": {
"ipaddress": "172.16.1.2", "ipaddress": "172.16.1.2",
"gateway": "172.16.1.1", "gateway": "172.16.1.1",
@ -473,6 +494,20 @@ test_data = {
"SecurityGroup": "SecurityGroupProvider" "SecurityGroup": "SecurityGroupProvider"
} }
}, },
"shared_network_config_drive_offering": {
"name": 'shared_network_config_drive_offering',
"displaytext": 'shared_network_config_drive_offering',
"guestiptype": 'shared',
"supportedservices": 'Dhcp,UserData',
"traffictype": 'GUEST',
"specifyVlan": "True",
"specifyIpRanges": "True",
"availability": 'Optional',
"serviceProviderList": {
"Dhcp": "VirtualRouter",
"UserData": 'ConfigDrive'
}
},
"shared_network_sg": { "shared_network_sg": {
"name": "Shared-Network-SG-Test", "name": "Shared-Network-SG-Test",
"displaytext": "Shared-Network_SG-Test", "displaytext": "Shared-Network_SG-Test",
@ -1744,8 +1779,8 @@ test_data = {
}, },
"test_34_DeployVM_in_SecondSGNetwork": { "test_34_DeployVM_in_SecondSGNetwork": {
"zone": "advsg", "zone": "advsg",
"config": "D:\ACS-Repo\setup\dev\\advancedsg.cfg", #Absolute path to cfg file "config": "D:\ACS-Repo\setup\dev\\advancedsg.cfg", # Absolute path to cfg file
#For sample configuration please refer to <ACS repo>/setup/dev/advancedsg.cfg # For sample configuration please refer to <ACS repo>/setup/dev/advancedsg.cfg
"template": "CentOS 5.3(64-bit) no GUI (Simulator)", "template": "CentOS 5.3(64-bit) no GUI (Simulator)",
"dbSvr": { "dbSvr": {
"dbSvr": "10.146.0.133", "dbSvr": "10.146.0.133",
@ -2277,10 +2312,10 @@ test_data = {
"network_all": { "network_all": {
"name": "SharedNetwork-All-nuage", "name": "SharedNetwork-All-nuage",
"displaytext": "SharedNetwork-All-nuage", "displaytext": "SharedNetwork-All-nuage",
"gateway": "10.223.1.1", "gateway": "10.200.100.1",
"netmask": "255.255.255.0", "netmask": "255.255.255.0",
"startip": "10.223.1.21", "startip": "10.200.100.21",
"endip": "10.223.1.100", "endip": "10.200.100.100",
"acltype": "Domain" "acltype": "Domain"
}, },
"network_domain_with_no_subdomain_access": { "network_domain_with_no_subdomain_access": {
@ -2313,10 +2348,10 @@ test_data = {
"acltype": "Account" "acltype": "Account"
}, },
"publiciprange1": { "publiciprange1": {
"gateway": "10.223.1.1", "gateway": "10.200.100.1",
"netmask": "255.255.255.0", "netmask": "255.255.255.0",
"startip": "10.223.1.101", "startip": "10.200.100.101",
"endip": "10.223.1.105", "endip": "10.200.100.105",
"forvirtualnetwork": "false" "forvirtualnetwork": "false"
}, },
"publiciprange2": { "publiciprange2": {
@ -2327,10 +2362,10 @@ test_data = {
"forvirtualnetwork": "false" "forvirtualnetwork": "false"
}, },
"publiciprange3": { "publiciprange3": {
"gateway": "10.223.1.1", "gateway": "10.200.100.1",
"netmask": "255.255.255.0", "netmask": "255.255.255.0",
"startip": "10.223.1.2", "startip": "10.200.100.2",
"endip": "10.223.1.20", "endip": "10.200.100.20",
"forvirtualnetwork": "false" "forvirtualnetwork": "false"
} }
} }

View File

@ -2422,7 +2422,7 @@
//p.s. Netscaler is supported in both vpc and non-vpc //p.s. Netscaler is supported in both vpc and non-vpc
if ($useVpc.is(':visible') && $useVpcCb.is(':checked')) { //*** vpc *** if ($useVpc.is(':visible') && $useVpcCb.is(':checked')) { //*** vpc ***
$optionsOfProviders.each(function(index) { $optionsOfProviders.each(function(index) {
if ($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter' || $(this).val() == 'Netscaler' || $(this).val() == 'NuageVsp' || $(this).val() == 'NuageVspVpc' || $(this).val() == 'BigSwitchBcf') { if ($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter' || $(this).val() == 'Netscaler' || $(this).val() == 'NuageVsp' || $(this).val() == 'NuageVspVpc' || $(this).val() == 'BigSwitchBcf' || $(this).val() == 'ConfigDrive') {
$(this).attr('disabled', false); $(this).attr('disabled', false);
} else { } else {
$(this).attr('disabled', true); $(this).attr('disabled', true);

View File

@ -7640,6 +7640,120 @@
} }
} }
} }
},
ConfigDrive: {
id: "ConfigDrive",
label: "ConfigDrive",
isMaximized: true,
type: 'detailView',
fields: {
name: {
label: 'label.name'
},
state: {
label: 'label.status',
indicator: {
'Enabled': 'on'
}
}
},
tabs: {
network: {
title: 'label.network',
fields: [{
name: {
label: 'label.name'
}
}, {
state: {
label: 'label.state'
},
supportedServices: {
label: 'label.supported.services'
},
id: {
label: 'label.id'
},
physicalnetworkid: {
label: 'label.physical.network.ID'
}
}],
dataProvider: function(args) {
refreshNspData("ConfigDrive");
args.response.success({
actionFilter: ovsProviderActionFilter,
data: $.extend(nspMap["ConfigDrive"], {
supportedServices: nspMap["ConfigDrive"] == undefined? "": nspMap["ConfigDrive"].servicelist.join(', ')
})
});
}
}
},
actions: {
enable: {
label: 'label.enable.provider',
action: function(args) {
$.ajax({
url: createURL("updateNetworkServiceProvider&id=" + nspMap["ConfigDrive"].id + "&state=Enabled"),
dataType: "json",
success: function(json) {
var jid = json.updatenetworkserviceproviderresponse.jobid;
args.response.success({
_custom: {
jobId: jid,
getUpdatedItem: function(json) {
$(window).trigger('cloudStack.fullRefresh');
}
}
});
}
});
},
messages: {
confirm: function(args) {
return 'message.confirm.enable.provider';
},
notification: function() {
return 'label.enable.provider';
}
},
notification: {
poll: pollAsyncJobResult
}
},
disable: {
label: 'label.disable.provider',
action: function(args) {
$.ajax({
url: createURL("updateNetworkServiceProvider&id=" + nspMap["ConfigDrive"].id + "&state=Disabled"),
dataType: "json",
success: function(json) {
var jid = json.updatenetworkserviceproviderresponse.jobid;
args.response.success({
_custom: {
jobId: jid,
getUpdatedItem: function(json) {
$(window).trigger('cloudStack.fullRefresh');
}
}
});
}
});
},
messages: {
confirm: function(args) {
return 'message.confirm.disable.provider';
},
notification: function() {
return 'label.disable.provider';
}
},
notification: {
poll: pollAsyncJobResult
}
}
}
} }
} }
} }
@ -22190,6 +22304,9 @@
case "GloboDns": case "GloboDns":
nspMap["GloboDns"] = items[i]; nspMap["GloboDns"] = items[i];
break; break;
case "ConfigDrive":
nspMap["ConfigDrive"] = items[i];
break;
} }
} }
} }
@ -22287,6 +22404,11 @@
name: 'GloboDNS', name: 'GloboDNS',
state: nspMap.GloboDns ? nspMap.GloboDns.state : 'Disabled' state: nspMap.GloboDns ? nspMap.GloboDns.state : 'Disabled'
}); });
nspHardcodingArray.push({
id: "ConfigDrive",
name: "ConfigDrive",
state: nspMap.ConfigDrive ? nspMap.ConfigDrive.state : 'Disabled'
});
//CLOUDSTACK-6840: OVS refers to SDN provider. However, we are not supporting SDN in this release. //CLOUDSTACK-6840: OVS refers to SDN provider. However, we are not supporting SDN in this release.
/* /*

View File

@ -116,6 +116,10 @@ import com.cloud.utils.script.Script;
public class VirtualMachineMO extends BaseMO { public class VirtualMachineMO extends BaseMO {
private static final Logger s_logger = Logger.getLogger(VirtualMachineMO.class); private static final Logger s_logger = Logger.getLogger(VirtualMachineMO.class);
private static final ExecutorService MonitorServiceExecutor = Executors.newCachedThreadPool(new NamedThreadFactory("VM-Question-Monitor")); private static final ExecutorService MonitorServiceExecutor = Executors.newCachedThreadPool(new NamedThreadFactory("VM-Question-Monitor"));
public static final String ANSWER_YES = "0";
public static final String ANSWER_NO = "1";
private ManagedObjectReference _vmEnvironmentBrowser = null; private ManagedObjectReference _vmEnvironmentBrowser = null;
public VirtualMachineMO(VmwareContext context, ManagedObjectReference morVm) { public VirtualMachineMO(VmwareContext context, ManagedObjectReference morVm) {
@ -1402,6 +1406,12 @@ public class VirtualMachineMO extends BaseMO {
// isoDatastorePath: [datastore name] isoFilePath // isoDatastorePath: [datastore name] isoFilePath
public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, boolean connect, boolean connectAtBoot) throws Exception { public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, boolean connect, boolean connectAtBoot) throws Exception {
attachIso(isoDatastorePath, morDs, connect, connectAtBoot, null);
}
// isoDatastorePath: [datastore name] isoFilePath
public void attachIso(String isoDatastorePath, ManagedObjectReference morDs,
boolean connect, boolean connectAtBoot, Integer key) throws Exception {
if (s_logger.isTraceEnabled()) if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath + ", datastore: " + s_logger.trace("vCenter API trace - attachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath + ", datastore: " +
@ -1411,7 +1421,12 @@ public class VirtualMachineMO extends BaseMO {
assert (morDs != null); assert (morDs != null);
boolean newCdRom = false; boolean newCdRom = false;
VirtualCdrom cdRom = (VirtualCdrom)getIsoDevice(); VirtualCdrom cdRom;
if (key == null) {
cdRom = (VirtualCdrom) getIsoDevice();
} else {
cdRom = (VirtualCdrom) getIsoDevice(key);
}
if (cdRom == null) { if (cdRom == null) {
newCdRom = true; newCdRom = true;
cdRom = new VirtualCdrom(); cdRom = new VirtualCdrom();
@ -1461,11 +1476,15 @@ public class VirtualMachineMO extends BaseMO {
s_logger.trace("vCenter API trace - detachIso() done(successfully)"); s_logger.trace("vCenter API trace - detachIso() done(successfully)");
} }
public void detachIso(String isoDatastorePath) throws Exception { public int detachIso(String isoDatastorePath) throws Exception {
return detachIso(isoDatastorePath, false);
}
public int detachIso(String isoDatastorePath, final boolean force) throws Exception {
if (s_logger.isTraceEnabled()) if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath); s_logger.trace("vCenter API trace - detachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath);
VirtualDevice device = getIsoDevice(); VirtualDevice device = getIsoDevice(isoDatastorePath);
if (device == null) { if (device == null) {
if (s_logger.isTraceEnabled()) if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachIso() done(failed)"); s_logger.trace("vCenter API trace - detachIso() done(failed)");
@ -1514,7 +1533,7 @@ public class VirtualMachineMO extends BaseMO {
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msg.getId())) { if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msg.getId())) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId() + s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId() +
", for safe operation we will automatically decline it"); ", for safe operation we will automatically decline it");
vmMo.answerVM(question.getId(), "1"); vmMo.answerVM(question.getId(), force ? ANSWER_YES : ANSWER_NO);
break; break;
} }
} }
@ -1531,7 +1550,7 @@ public class VirtualMachineMO extends BaseMO {
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msgId)) { if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msgId)) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + question.getId() + s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + question.getId() +
". Message id : " + msgId + ". Message text : " + msgText + ", for safe operation we will automatically decline it."); ". Message id : " + msgId + ". Message text : " + msgText + ", for safe operation we will automatically decline it.");
vmMo.answerVM(question.getId(), "1"); vmMo.answerVM(question.getId(), force ? ANSWER_YES : ANSWER_NO);
} }
} }
@ -1570,6 +1589,7 @@ public class VirtualMachineMO extends BaseMO {
flags[0] = true; flags[0] = true;
future.cancel(true); future.cancel(true);
} }
return device.getKey();
} }
public Pair<VmdkFileDescriptor, byte[]> getVmdkFileInfo(String vmdkDatastorePath) throws Exception { public Pair<VmdkFileDescriptor, byte[]> getVmdkFileInfo(String vmdkDatastorePath) throws Exception {
@ -2826,6 +2846,32 @@ public class VirtualMachineMO extends BaseMO {
return null; return null;
} }
public VirtualDevice getIsoDevice(int key) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualCdrom && device.getKey() == key) {
return device;
}
}
}
return null;
}
public VirtualDevice getIsoDevice(String filename) throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if(devices != null && devices.size() > 0) {
for(VirtualDevice device : devices) {
if(device instanceof VirtualCdrom && device.getBacking() instanceof VirtualCdromIsoBackingInfo &&
((VirtualCdromIsoBackingInfo)device.getBacking()).getFileName().equals(filename)) {
return device;
}
}
}
return null;
}
public int getNextDeviceNumber(int controllerKey) throws Exception { public int getNextDeviceNumber(int controllerKey) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
@ -2982,7 +3028,7 @@ public class VirtualMachineMO extends BaseMO {
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msg.getId())) { if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msg.getId())) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId() + s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId() +
", for safe operation we will automatically decline it"); ", for safe operation we will automatically decline it");
vmMo.answerVM(question.getId(), "1"); vmMo.answerVM(question.getId(), ANSWER_NO);
break; break;
} }
} }
@ -2999,7 +3045,7 @@ public class VirtualMachineMO extends BaseMO {
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msgId)) { if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msgId)) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + question.getId() + s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + question.getId() +
". Message id : " + msgId + ". Message text : " + msgText + ", for safe operation we will automatically decline it."); ". Message id : " + msgId + ". Message text : " + msgText + ", for safe operation we will automatically decline it.");
vmMo.answerVM(question.getId(), "1"); vmMo.answerVM(question.getId(), ANSWER_NO);
} }
} }