package net.juniper.contrail.vcenter;
import java.rmi.RemoteException;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentSkipListMap;
import org.apache.log4j.Logger;
import com.google.common.net.InetAddresses;
import com.vmware.vim25.DVPortSetting;
import com.vmware.vim25.DistributedVirtualSwitchKeyedOpaqueBlob;
import com.vmware.vim25.Event;
import com.vmware.vim25.IpPool;
import com.vmware.vim25.IpPoolIpPoolConfigInfo;
import com.vmware.vim25.RuntimeFault;
import com.vmware.vim25.VMwareDVSPortSetting;
import com.vmware.vim25.VMwareDVSPvlanMapEntry;
import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanSpec;
import com.vmware.vim25.mo.DistributedVirtualPortgroup;
import com.vmware.vim25.mo.ManagedObject;
import com.vmware.vim25.mo.util.PropertyCollectorUtil;
import net.juniper.contrail.api.ObjectReference;
import net.juniper.contrail.api.types.VnSubnetsType;
import net.juniper.contrail.api.types.VnSubnetsType.IpamSubnetType;
public class VirtualNetworkInfo extends VCenterObject {
private final Logger s_logger =
Logger.getLogger(VirtualNetworkInfo.class);
private String uuid; // required attribute, key for this object
private String name;
private short primaryVlanId;
private short isolatedVlanId;
private SortedMap<String, VirtualMachineInterfaceInfo> vmiInfoMap; // key is MAC address
private Integer ipPoolId;
private String subnetAddress;
private String subnetMask;
private String gatewayAddress;
private boolean ipPoolEnabled;
private String range;
private boolean externalIpam;
// Vmware
com.vmware.vim25.mo.Network net;
DistributedVirtualPortgroup dpg;
com.vmware.vim25.mo.VmwareDistributedVirtualSwitch dvs;
String dvsName;
com.vmware.vim25.mo.Datacenter dc;
String dcName;
// API server
net.juniper.contrail.api.types.VirtualNetwork apiVn;
public VirtualNetworkInfo(String uuid) {
if (uuid == null) {
throw new IllegalArgumentException("Cannot init VN with null uuid");
}
this.uuid = uuid;
vmiInfoMap = new ConcurrentSkipListMap<String, VirtualMachineInterfaceInfo>();
}
public VirtualNetworkInfo(String uuid, String name,
boolean externalIpam, boolean ipPoolEnabled,
String subnetAddress,String subnetMask, String range, String gatewayAddress,
short primaryVlanId, short isolatedVlanId)
{
this.uuid = uuid;
this.name = name;
this.externalIpam = externalIpam;
this.ipPoolEnabled = ipPoolEnabled;
this.subnetAddress = subnetAddress;
this.subnetMask = subnetMask;
this.range = range;
this.gatewayAddress = gatewayAddress;
this.primaryVlanId = primaryVlanId;
this.isolatedVlanId = isolatedVlanId;
vmiInfoMap = new ConcurrentSkipListMap<String, VirtualMachineInterfaceInfo>();
}
public VirtualNetworkInfo(VirtualNetworkInfo vnInfo)
{
if (vnInfo == null) {
throw new IllegalArgumentException("Cannot init VN from null VN");
}
this.uuid = vnInfo.uuid;
this.name = vnInfo.name;
this.primaryVlanId = vnInfo.primaryVlanId;
this.isolatedVlanId = vnInfo.isolatedVlanId;
this.vmiInfoMap = vnInfo.vmiInfoMap;
this.ipPoolId = vnInfo.ipPoolId;
this.subnetAddress = vnInfo.subnetAddress;
this.subnetMask = vnInfo.subnetMask;
this.gatewayAddress = vnInfo.gatewayAddress;
this.ipPoolEnabled = vnInfo.ipPoolEnabled;
this.range = vnInfo.range;
this.externalIpam = vnInfo.externalIpam;
this.net = vnInfo.net;
this.dpg = vnInfo.dpg;
this.dvs = vnInfo.dvs;
this.dvsName = vnInfo.dvsName;
this.dc = vnInfo.dc;
this.dcName = vnInfo.dcName;
this.apiVn = vnInfo.apiVn;
}
public VirtualNetworkInfo(net.juniper.contrail.api.types.VirtualNetwork vn) {
if (vn == null) {
throw new IllegalArgumentException("Cannot init VN from null API VN");
}
apiVn = vn;
uuid = vn.getUuid();
name = vn.getName();
vmiInfoMap = new ConcurrentSkipListMap<String, VirtualMachineInterfaceInfo>();
externalIpam = vn.getExternalIpam();
readIpAm();
}
private void readIpAm() {
List<ObjectReference<VnSubnetsType>> objList = apiVn.getNetworkIpam();
if (objList != null) {
for (ObjectReference<VnSubnetsType> objRef: Utils.safe(objList)) {
if (objRef == null) {
continue;
}
VnSubnetsType subnetsType = objRef.getAttr();
List<IpamSubnetType> ipamsubList = subnetsType.getIpamSubnets();
if (ipamsubList == null) {
continue;
}
for (IpamSubnetType sub: ipamsubList) {
if (sub == null) {
continue;
}
subnetAddress = sub.getSubnet().getIpPrefix();
int len = sub.getSubnet().getIpPrefixLen();
int mask = 0xFFFFFFFF << (32 - len);
subnetMask = InetAddresses.fromInteger(mask).getHostAddress();
gatewayAddress = sub.getDefaultGateway();
}
}
}
}
@SuppressWarnings("rawtypes")
public VirtualNetworkInfo(Event event, VCenterDB vcenterDB, VncDB vncDB) throws Exception {
vmiInfoMap = new ConcurrentSkipListMap<String, VirtualMachineInterfaceInfo>();
if (event.getDatacenter() != null) {
dcName = event.getDatacenter().getName();
dc = vcenterDB.getVmwareDatacenter(dcName);
}
if (event.getDvs() != null) {
dvsName = event.getDvs().getName();
dvs = vcenterDB.getVmwareDvs(dvsName, dc, dcName);
} else {
dvsName = vcenterDB.contrailDvSwitchName;
dvs = vcenterDB.getVmwareDvs(dvsName, dc, dcName);
}
if (event.getNet() != null) {
name = event.getNet().getName();
net = vcenterDB.getVmwareNetwork(name, dvs, dvsName, dcName);
}
dpg = vcenterDB.getVmwareDpg(name, dvs, dvsName, dcName);
ManagedObject mo[] = new ManagedObject[1];
mo[0] = dpg;
Hashtable[] pTables = PropertyCollectorUtil.retrieveProperties(mo,
"DistributedVirtualPortgroup",
new String[] {"name",
"config.key",
"config.defaultPortConfig",
"config.vendorSpecificConfig",
"summary.ipPoolId",
"summary.ipPoolName",
});
if (pTables == null || pTables[0] == null) {
throw new RemoteException("Could not read properties for network " + name);
}
populateInfo(vcenterDB, pTables[0]);
if (vcenterDB.mode == Mode.VCENTER_AS_COMPUTE) {
apiVn = vncDB.findVirtualNetwork(uuid);
readIpAm();
}
}
@SuppressWarnings("rawtypes")
public VirtualNetworkInfo(VCenterDB vcenterDB,
DistributedVirtualPortgroup dpg, Hashtable pTable,
com.vmware.vim25.mo.Datacenter dc, String dcName,
com.vmware.vim25.mo.VmwareDistributedVirtualSwitch dvs,
String dvsName) throws Exception {
if (vcenterDB == null || dpg == null || pTable == null
|| dvs == null || dvsName == null
|| dc == null || dcName == null) {
throw new IllegalArgumentException("Cannot init VN from null arguments");
}
vmiInfoMap = new ConcurrentSkipListMap<String, VirtualMachineInterfaceInfo>();
this.dc = dc;
this.dcName = dcName;
this.dpg = dpg;
this.dvs = dvs;
this.dvsName = dvsName;
populateInfo(vcenterDB, pTable);
}
@SuppressWarnings("rawtypes")
void populateInfo(VCenterDB vcenterDB, Hashtable pTable)
throws Exception {
name = (String) pTable.get("name");
switch(vcenterDB.mode) {
case VCENTER_AS_COMPUTE:
/*
From Mitaka nova driver will append cluster_id to port group
therefore need to extract the appended cluster id
*/
name = name.substring(Math.max(0, name.length() - 36));
// UUID is allocated by OpenStack and saved in the name field
uuid = name;
break;
case VCENTER_ONLY:
// UUID is allocated by the plugin
String key = (String) pTable.get("config.key");
byte[] vnKeyBytes = key.getBytes();
uuid = UUID.nameUUIDFromBytes(vnKeyBytes).toString();
break;
default:
throw new Exception("Unhandled mode " + vcenterDB.mode.name());
}
populateVlans(vcenterDB, pTable);
populateAddressManagement(vcenterDB, pTable);
}
@SuppressWarnings("rawtypes")
private void populateAddressManagement(VCenterDB vcenterDB,
Hashtable pTable)
throws RuntimeFault, RemoteException, Exception {
setIpPoolId( (Integer) pTable.get("summary.ipPoolId"), vcenterDB);
// Read externalIpam flag from custom field
DistributedVirtualSwitchKeyedOpaqueBlob[] opaqueBlobs = null;
Object obj = pTable.get("config.vendorSpecificConfig");
if (obj instanceof DistributedVirtualSwitchKeyedOpaqueBlob[]) {
opaqueBlobs = (DistributedVirtualSwitchKeyedOpaqueBlob[]) obj;
}
externalIpam = vcenterDB.getExternalIpamInfo(opaqueBlobs, name);
}
@SuppressWarnings("rawtypes")
private void populateVlans(VCenterDB vcenterDB, Hashtable pTable) throws Exception {
VMwareDVSPvlanMapEntry[] pvlanMapArray = vcenterDB.getDvsPvlanMap(dvsName, dc, dcName);
if (pvlanMapArray == null) {
s_logger.error(this + "Cannot populate vlan, private vlan not configured on dvSwitch: " + dvsName +
" Datacenter: " + dcName);
return;
}
// Extract dvPg configuration info and port setting
DVPortSetting portSetting = (DVPortSetting) pTable.get("config.defaultPortConfig");
if (!(portSetting instanceof VMwareDVSPortSetting)) {
s_logger.error(this + " Cannot populate vlan, invalid port setting: " + portSetting);
return;
}
VMwareDVSPortSetting vPortSetting = (VMwareDVSPortSetting) portSetting;
VmwareDistributedVirtualSwitchVlanSpec vlanSpec = vPortSetting.getVlan();
if (vlanSpec instanceof VmwareDistributedVirtualSwitchPvlanSpec) {
// config.defaultPortConfig.vlan contains isolated secondary VLAN Id
VmwareDistributedVirtualSwitchPvlanSpec pvlanSpec =
(VmwareDistributedVirtualSwitchPvlanSpec) vlanSpec;
isolatedVlanId = (short)pvlanSpec.getPvlanId();
// Find primaryVLAN corresponding to isolated secondary VLAN
// by searching in the pvlan Map Array
for (short i=0; i < pvlanMapArray.length; i++) {
if (pvlanMapArray[i].getPvlanType().equals("isolated")
&& (short)pvlanMapArray[i].getSecondaryVlanId() == isolatedVlanId) {
primaryVlanId = (short)pvlanMapArray[i].getPrimaryVlanId();
s_logger.debug(this + " VlanType = PrivateVLAN"
+ " PrimaryVLAN = " + primaryVlanId
+ " IsolatedVLAN = " + isolatedVlanId);
return;
}
}
} else if (vlanSpec instanceof VmwareDistributedVirtualSwitchVlanIdSpec) {
VmwareDistributedVirtualSwitchVlanIdSpec vlanIdSpec =
(VmwareDistributedVirtualSwitchVlanIdSpec) vlanSpec;
primaryVlanId = isolatedVlanId = (short)vlanIdSpec.getVlanId();
s_logger.info(this + " VlanType = VLAN " + " VlanId = " + primaryVlanId);
} else {
s_logger.error(this + " Cannot populate vlan, invalid vlan spec: " + vlanSpec);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public short getIsolatedVlanId() {
return isolatedVlanId;
}
public void setIsolatedVlanId(short vlanId) {
this.isolatedVlanId = vlanId;
}
public short getPrimaryVlanId() {
return primaryVlanId;
}
public void setPrimaryVlanId(short vlanId) {
this.primaryVlanId = vlanId;
}
// "virtual-network"s parent is "project"
public String getProjectUuid() {
return apiVn.getParentUuid();
}
public boolean getIpPoolEnabled() {
return ipPoolEnabled;
}
public void setIpPoolEnabled(boolean _ipPoolEnabled) {
this.ipPoolEnabled = _ipPoolEnabled;
}
public String getRange() {
return range;
}
public void setRange(String _range) {
this.range = _range;
}
public SortedMap<String, VirtualMachineInterfaceInfo> getVmiInfo() {
return vmiInfoMap;
}
public Integer getIpPoolId() {
return ipPoolId;
}
public void setIpPoolId(Integer poolId, VCenterDB vcenterDB)
throws RuntimeFault, RemoteException {
IpPool ipPool = vcenterDB.getIpPoolById(poolId, name, dc, dcName);
if (ipPool != null) {
IpPoolIpPoolConfigInfo ipConfigInfo = ipPool.getIpv4Config();
subnetAddress = ipConfigInfo.getSubnetAddress();
subnetMask = ipConfigInfo.getNetmask();
gatewayAddress = ipConfigInfo.getGateway();
ipPoolEnabled = ipConfigInfo.getIpPoolEnabled();
range = ipConfigInfo.getRange();
this.ipPoolId = ipPool.id;
s_logger.info("Set ipPoolId to " + ipPoolId + " for " + this);
} else {
subnetAddress = null;
subnetMask = null;
gatewayAddress = null;
ipPoolEnabled = false;
range = null;
this.ipPoolId = null;
s_logger.info("Set ipPoolId to null for " + this);
}
}
public String getSubnetAddress() {
return subnetAddress;
}
public void setSubnetAddress(String subnetAddress) {
this.subnetAddress = subnetAddress;
}
public String getSubnetMask() {
return subnetMask;
}
public void setSubnetMask(String subnetMask) {
this.subnetMask = subnetMask;
}
public String getGatewayAddress() {
return gatewayAddress;
}
public void setGatewayAddress(String gatewayAddress) {
this.gatewayAddress = gatewayAddress;
}
public boolean getExternalIpam() {
return externalIpam;
}
public void setExternalIpam(boolean externalIpam) {
this.externalIpam = externalIpam;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public DistributedVirtualPortgroup getDpg() {
return dpg;
}
public void setDpg(DistributedVirtualPortgroup dpg) {
this.dpg = dpg;
}
public void created(VirtualMachineInterfaceInfo vmiInfo) {
vmiInfoMap.put(vmiInfo.getMacAddress(), vmiInfo);
}
public void updated(VirtualMachineInterfaceInfo vmiInfo) {
if (!vmiInfoMap.containsKey(vmiInfo.getMacAddress())) {
vmiInfoMap.put(vmiInfo.getMacAddress(), vmiInfo);
}
}
public void deleted(VirtualMachineInterfaceInfo vmiInfo) {
if (vmiInfoMap.containsKey(vmiInfo.getMacAddress())) {
vmiInfoMap.remove(vmiInfo.getMacAddress());
}
}
public boolean contains(VirtualMachineInterfaceInfo vmiInfo) {
return vmiInfoMap.containsKey(vmiInfo.getMacAddress());
}
public boolean equals(VirtualNetworkInfo vn) {
if (vn == null) {
return false;
}
if (name != null && !name.equals(vn.name)
|| name == null && vn.name != null) {
return false;
}
if (uuid != null && !uuid.equals(vn.uuid)
|| uuid == null && vn.uuid != null) {
return false;
}
if (isolatedVlanId != vn.isolatedVlanId
|| primaryVlanId != vn.primaryVlanId
|| ipPoolEnabled != vn.ipPoolEnabled
|| externalIpam != vn.externalIpam) {
return false;
}
if (subnetAddress != null && !subnetAddress.equals(vn.subnetAddress)
|| subnetAddress == null && vn.subnetAddress != null) {
return false;
}
if (subnetMask != null && !subnetMask.equals(vn.subnetMask)
|| subnetMask == null && vn.subnetMask != null) {
return false;
}
if (gatewayAddress != null && !gatewayAddress.equals(vn.gatewayAddress)
|| gatewayAddress == null && vn.gatewayAddress != null) {
return false;
}
if (range != null && !range.equals(vn.range)
|| range == null && vn.range != null) {
return false;
}
return true;
}
public String toString() {
return "VN <" + name + ", " + uuid + ">";
}
public StringBuffer toStringBuffer() {
StringBuffer s = new StringBuffer(
"VN <" + name + ", " + uuid + ">\n\n");
for (Map.Entry<String, VirtualMachineInterfaceInfo> entry:
vmiInfoMap.entrySet()) {
VirtualMachineInterfaceInfo vmiInfo = entry.getValue();
s.append("\t")
.append(vmiInfo).append("\n");
}
s.append("\n");
return s;
}
boolean ignore() {
// Ignore networks that do not have PVLAN/VLAN configured
return (primaryVlanId == 0 && isolatedVlanId == 0);
}
// change the method below to use Observer pattern and get rid of the Vnc param
@Override
void create(VncDB vncDB) throws Exception {
if (ignore()) {
return;
}
vncDB.createVirtualNetwork(this);
// notify observers
MainDB.created(this);
}
@Override
void update(VCenterObject obj, VncDB vncDB)
throws Exception {
if (ignore()) {
return;
}
VirtualNetworkInfo newVnInfo = (VirtualNetworkInfo)obj;
name = newVnInfo.name;
primaryVlanId = newVnInfo.primaryVlanId;
isolatedVlanId = newVnInfo.isolatedVlanId;
ipPoolId = newVnInfo.ipPoolId;
subnetAddress = newVnInfo.subnetAddress;
subnetMask = newVnInfo.subnetMask;
gatewayAddress = newVnInfo.gatewayAddress;
ipPoolEnabled = newVnInfo.ipPoolEnabled;
range = newVnInfo.range;
externalIpam = newVnInfo.externalIpam;
vncDB.updateVirtualNetwork(this);
}
@Override
void sync(VCenterObject obj, VncDB vncDB)
throws Exception {
VirtualNetworkInfo oldVnInfo = (VirtualNetworkInfo)obj;
if (uuid == null && oldVnInfo.uuid != null) {
uuid = oldVnInfo.uuid;
}
if (name == null && oldVnInfo.name != null) {
name = oldVnInfo.name;
}
if (isolatedVlanId == 0 && oldVnInfo.isolatedVlanId != 0) {
isolatedVlanId = oldVnInfo.isolatedVlanId;
}
if (primaryVlanId == 0 && oldVnInfo.primaryVlanId != 0) {
primaryVlanId = oldVnInfo.primaryVlanId;
}
if (ipPoolId == null && oldVnInfo.ipPoolId != null) {
ipPoolId = oldVnInfo.ipPoolId;
}
if (subnetAddress == null && oldVnInfo.subnetAddress != null) {
subnetAddress = oldVnInfo.subnetAddress;
externalIpam = oldVnInfo.externalIpam;
}
if (subnetMask == null && oldVnInfo.subnetMask != null) {
subnetMask = oldVnInfo.subnetMask;
}
if (gatewayAddress == null && oldVnInfo.gatewayAddress != null) {
gatewayAddress = oldVnInfo.gatewayAddress;
}
if (range == null && oldVnInfo.range != null) {
range = oldVnInfo.range;
ipPoolEnabled = oldVnInfo.ipPoolEnabled;
}
if (apiVn == null && oldVnInfo.apiVn != null) {
apiVn = oldVnInfo.apiVn;
}
}
@Override
void delete(VncDB vncDB)
throws Exception {
for (Map.Entry<String, VirtualMachineInterfaceInfo> entry:
vmiInfoMap.entrySet()) {
VirtualMachineInterfaceInfo vmiInfo = entry.getValue();
vmiInfo.delete(vncDB);
}
vncDB.deleteVirtualNetwork(this);
MainDB.deleted(this);
}
public boolean isIpAddressInSubnetAndRange(String ipAddress) {
if (ipAddress == null) {
return true;
}
if (subnetAddress == null || subnetMask == null) {
return false;
}
int addr = InetAddresses.coerceToInteger(InetAddresses.forString(ipAddress));
int subnet = InetAddresses.coerceToInteger(InetAddresses.forString(subnetAddress));
int mask = InetAddresses.coerceToInteger(InetAddresses.forString(subnetMask));
if (((addr & mask) != subnet)) {
return false;
}
if (!ipPoolEnabled || range == null || range.isEmpty()) {
return true;
}
String[] pools = range.split("\\#");
if (pools.length == 2) {
String start = (pools[0]).replace(" ","");
String num = (pools[1]).replace(" ","");
int start_ip = InetAddresses.coerceToInteger(InetAddresses.forString(start));
int start_range = start_ip & ~mask;
int end_ip = start_ip + Integer.parseInt(num) - 1;
int end_range = end_ip & ~mask;
int host = addr & ~mask;
return (start_range <= host) && (host <= end_range);
}
return true;
}
}