cloudstack/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
2011-06-08 17:42:04 -07:00

3055 lines
126 KiB
Java
Executable File

/**
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.configuration;
import java.net.URI;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.acl.SecurityChecker;
import com.cloud.alert.AlertManager;
import com.cloud.api.commands.CreateCfgCmd;
import com.cloud.api.commands.CreateDiskOfferingCmd;
import com.cloud.api.commands.CreateNetworkOfferingCmd;
import com.cloud.api.commands.CreatePodCmd;
import com.cloud.api.commands.CreateServiceOfferingCmd;
import com.cloud.api.commands.CreateVlanIpRangeCmd;
import com.cloud.api.commands.CreateZoneCmd;
import com.cloud.api.commands.DeleteDiskOfferingCmd;
import com.cloud.api.commands.DeleteNetworkOfferingCmd;
import com.cloud.api.commands.DeletePodCmd;
import com.cloud.api.commands.DeleteServiceOfferingCmd;
import com.cloud.api.commands.DeleteVlanIpRangeCmd;
import com.cloud.api.commands.DeleteZoneCmd;
import com.cloud.api.commands.ListNetworkOfferingsCmd;
import com.cloud.api.commands.UpdateCfgCmd;
import com.cloud.api.commands.UpdateDiskOfferingCmd;
import com.cloud.api.commands.UpdateNetworkOfferingCmd;
import com.cloud.api.commands.UpdatePodCmd;
import com.cloud.api.commands.UpdateServiceOfferingCmd;
import com.cloud.api.commands.UpdateZoneCmd;
import com.cloud.configuration.ResourceCount.ResourceType;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.AccountVlanMapVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.dc.DataCenterIpAddressVO;
import com.cloud.dc.DataCenterLinkLocalIpAddressVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.Pod;
import com.cloud.dc.PodVlanMapVO;
import com.cloud.dc.Vlan;
import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.AccountVlanMapDao;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.DataCenterIpAddressDao;
import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDaoImpl;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.dc.dao.PodVlanMapDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.event.dao.EventDao;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.Network;
import com.cloud.network.Network.GuestIpType;
import com.cloud.network.NetworkManager;
import com.cloud.network.NetworkVO;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.NetworkOffering.Availability;
import com.cloud.offering.ServiceOffering;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.org.Grouping;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.test.IPRangeConfig;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserContext;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.Adapters;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Inject;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.ConsoleProxyDao;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.SecondaryStorageVmDao;
import com.cloud.vm.dao.VMInstanceDao;
@Local(value = { ConfigurationManager.class, ConfigurationService.class })
public class ConfigurationManagerImpl implements ConfigurationManager, ConfigurationService {
public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class.getName());
String _name;
@Inject
ConfigurationDao _configDao;
@Inject
HostPodDao _podDao;
@Inject
AccountVlanMapDao _accountVlanMapDao;
@Inject
PodVlanMapDao _podVlanMapDao;
@Inject
DataCenterDao _zoneDao;
@Inject
DomainRouterDao _domrDao;
@Inject
DomainDao _domainDao;
@Inject
ServiceOfferingDao _serviceOfferingDao;
@Inject
DiskOfferingDao _diskOfferingDao;
@Inject
NetworkOfferingDao _networkOfferingDao;
@Inject
VlanDao _vlanDao;
@Inject
IPAddressDao _publicIpAddressDao;
@Inject
DataCenterIpAddressDao _privateIpAddressDao;
@Inject
VMInstanceDao _vmInstanceDao;
@Inject
AccountDao _accountDao;
@Inject
EventDao _eventDao;
@Inject
UserDao _userDao;
@Inject
NetworkDao _networkDao;
@Inject
ConsoleProxyDao _consoleDao;
@Inject
SecondaryStorageVmDao _secStorageDao;
@Inject
AccountManager _accountMgr;
@Inject
NetworkManager _networkMgr;
@Inject
ClusterDao _clusterDao;
@Inject
AlertManager _alertMgr;
@Inject(adapter = SecurityChecker.class)
Adapters<SecurityChecker> _secChecker;
// FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao?
protected static final DataCenterLinkLocalIpAddressDaoImpl _LinkLocalIpAllocDao = ComponentLocator.inject(DataCenterLinkLocalIpAddressDaoImpl.class);
private int _maxVolumeSizeInGb;
private long _defaultPageSize;
protected Set<String> configValuesForValidation;
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
_name = name;
String maxVolumeSizeInGbString = _configDao.getValue("storage.max.volume.size");
_maxVolumeSizeInGb = NumbersUtil.parseInt(maxVolumeSizeInGbString, 2000);
String defaultPageSizeString = _configDao.getValue("default.page.size");
_defaultPageSize = NumbersUtil.parseLong(defaultPageSizeString, 500L);
populateConfigValuesForValidationSet();
return true;
}
private void populateConfigValuesForValidationSet() {
configValuesForValidation = new HashSet<String>();
configValuesForValidation.add("account.cleanup.interval");
configValuesForValidation.add("alert.wait");
configValuesForValidation.add("consoleproxy.capacityscan.interval");
configValuesForValidation.add("consoleproxy.loadscan.interval");
configValuesForValidation.add("expunge.interval");
configValuesForValidation.add("host.stats.interval");
configValuesForValidation.add("investigate.retry.interval");
configValuesForValidation.add("migrate.retry.interval");
configValuesForValidation.add("network.gc.interval");
configValuesForValidation.add("ping.interval");
configValuesForValidation.add("router.stats.interval");
configValuesForValidation.add("snapshot.poll.interval");
configValuesForValidation.add("stop.retry.interval");
configValuesForValidation.add("storage.stats.interval");
configValuesForValidation.add("storage.cleanup.interval");
configValuesForValidation.add("wait");
configValuesForValidation.add("xen.heartbeat.interval");
}
@Override
public String getName() {
return _name;
}
@Override
public boolean start() {
// TODO : this may not be a good place to do integrity check here, we put it here as we need _alertMgr to be properly
// configured
// before we can use it
// As it is so common for people to forget about configuring management.network.cidr,
String mgtCidr = _configDao.getValue(Config.ManagementNetwork.key());
if (mgtCidr == null || mgtCidr.trim().isEmpty()) {
String[] localCidrs = NetUtils.getLocalCidrs();
if (localCidrs.length > 0) {
s_logger.warn("Management network CIDR is not configured originally. Set it default to " + localCidrs[0]);
_alertMgr.sendAlert(AlertManager.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), "Management network CIDR is not configured originally. Set it default to " + localCidrs[0], "");
_configDao.update(Config.ManagementNetwork.key(), localCidrs[0]);
} else {
s_logger.warn("Management network CIDR is not properly configured and we are not able to find a default setting");
_alertMgr.sendAlert(AlertManager.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), "Management network CIDR is not properly configured and we are not able to find a default setting", "");
}
}
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public void updateConfiguration(long userId, String name, String value) {
if (value != null && (value.trim().isEmpty() || value.equals("null"))) {
value = null;
}
String validationMsg = validateConfigurationValue(name, value);
if (validationMsg != null) {
s_logger.error("Invalid configuration option, name: " + name + ", value:" + value);
throw new InvalidParameterValueException(validationMsg);
}
if (!_configDao.update(name, value)) {
s_logger.error("Failed to update configuration option, name: " + name + ", value:" + value);
throw new CloudRuntimeException("Failed to update configuration value. Please contact Cloud Support.");
}
if (Config.SystemVMUseLocalStorage.key().equalsIgnoreCase(name)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Config 'system.vm.use.local.storage' changed to value:" + value + ", need to update System VM offerings");
}
boolean useLocalStorage = Boolean.parseBoolean(_configDao.getValue(Config.SystemVMUseLocalStorage.key()));
ServiceOfferingVO serviceOffering = _serviceOfferingDao.findByName("Cloud.com-ConsoleProxy");
if (serviceOffering != null) {
serviceOffering.setUseLocalStorage(useLocalStorage);
if (!_serviceOfferingDao.update(serviceOffering.getId(), serviceOffering)) {
s_logger.error("Failed to update ConsoleProxy offering's use_local_storage option to value:" + useLocalStorage);
}
}
serviceOffering = _serviceOfferingDao.findByName("Cloud.Com-SoftwareRouter");
if (serviceOffering != null) {
serviceOffering.setUseLocalStorage(useLocalStorage);
if (!_serviceOfferingDao.update(serviceOffering.getId(), serviceOffering)) {
s_logger.error("Failed to update SoftwareRouter offering's use_local_storage option to value:" + useLocalStorage);
}
}
serviceOffering = _serviceOfferingDao.findByName("Cloud.com-SecondaryStorage");
if (serviceOffering != null) {
serviceOffering.setUseLocalStorage(useLocalStorage);
if (!_serviceOfferingDao.update(serviceOffering.getId(), serviceOffering)) {
s_logger.error("Failed to update SecondaryStorage offering's use_local_storage option to value:" + useLocalStorage);
}
}
}
}
@Override
public Configuration updateConfiguration(UpdateCfgCmd cmd) {
Long userId = UserContext.current().getCallerUserId();
String name = cmd.getCfgName();
String value = cmd.getValue();
// check if config value exists
if (_configDao.findByName(name) == null) {
throw new InvalidParameterValueException("Config parameter with name " + name + " doesn't exist");
}
if (value == null) {
return _configDao.findByName(name);
}
updateConfiguration(userId, name, value);
if (_configDao.getValue(name).equalsIgnoreCase(value)) {
return _configDao.findByName(name);
} else {
throw new CloudRuntimeException("Unable to update configuration parameter " + name);
}
}
private String validateConfigurationValue(String name, String value) {
if (value == null) {
return null;
}
Config c = Config.getConfig(name);
value = value.trim();
if (c == null) {
s_logger.error("Missing configuration variable " + name + " in configuration table");
return "Invalid configuration variable.";
}
Class<?> type = c.getType();
if (type.equals(Boolean.class)) {
if (!(value.equals("true") || value.equals("false"))) {
s_logger.error("Configuration variable " + name + " is expecting true or false in stead of " + value);
return "Please enter either 'true' or 'false'.";
}
return null;
}
if (type.equals(Integer.class) && configValuesForValidation.contains(name)) {
try {
int val = Integer.parseInt(value);
if (val <= 0) {
throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name);
}
} catch (NumberFormatException e) {
s_logger.error("There was an error trying to parse the integer value for:" + name);
throw new InvalidParameterValueException("There was an error trying to parse the integer value for:" + name);
}
}
String range = c.getRange();
if (range == null) {
return null;
}
if (type.equals(String.class)) {
if (range.equals("privateip")) {
try {
if (!NetUtils.isSiteLocalAddress(value)) {
s_logger.error("privateip range " + value + " is not a site local address for configuration variable " + name);
return "Please enter a site local IP address.";
}
} catch (NullPointerException e) {
s_logger.error("Error parsing ip address for " + name);
throw new InvalidParameterValueException("Error parsing ip address");
}
} else if (range.equals("netmask")) {
if (!NetUtils.isValidNetmask(value)) {
s_logger.error("netmask " + value + " is not a valid net mask for configuration variable " + name);
return "Please enter a valid netmask.";
}
} else if (range.equals("hypervisorList")) {
String[] hypervisors = value.split(",");
if (hypervisors == null) {
return "Please enter hypervisor list, seperated by comma";
}
for (String hypervisor : hypervisors) {
if (HypervisorType.getType(hypervisor) == HypervisorType.Any || HypervisorType.getType(hypervisor) == HypervisorType.None) {
return "Please enter valid hypervisor type";
}
}
} else {
String[] options = range.split(",");
for (String option : options) {
if (option.trim().equals(value)) {
return null;
}
}
s_logger.error("configuration value for " + name + " is invalid");
return "Please enter : " + range;
}
} else if (type.equals(Integer.class)) {
String[] options = range.split("-");
if (options.length != 2) {
String msg = "configuration range " + range + " for " + name + " is invalid";
s_logger.error(msg);
return msg;
}
int min = Integer.parseInt(options[0]);
int max = Integer.parseInt(options[1]);
int val = Integer.parseInt(value);
if (val < min || val > max) {
s_logger.error("configuration value for " + name + " is invalid");
return "Please enter : " + range;
}
}
return null;
}
private boolean podHasAllocatedPrivateIPs(long podId) {
HostPodVO pod = _podDao.findById(podId);
int count = _privateIpAddressDao.countIPs(podId, pod.getDataCenterId(), true);
return (count > 0);
}
@DB
protected void checkIfPodIsDeletable(long podId) {
List<List<String>> tablesToCheck = new ArrayList<List<String>>();
HostPodVO pod = _podDao.findById(podId);
// Check if there are allocated private IP addresses in the pod
if (_privateIpAddressDao.countIPs(podId, pod.getDataCenterId(), true) != 0) {
throw new CloudRuntimeException("There are private IP addresses allocated for this pod");
}
List<String> volumes = new ArrayList<String>();
volumes.add(0, "volumes");
volumes.add(1, "pod_id");
volumes.add(2, "there are storage volumes for this pod");
tablesToCheck.add(volumes);
List<String> host = new ArrayList<String>();
host.add(0, "host");
host.add(1, "pod_id");
host.add(2, "there are servers running in this pod");
tablesToCheck.add(host);
List<String> vmInstance = new ArrayList<String>();
vmInstance.add(0, "vm_instance");
vmInstance.add(1, "pod_id");
vmInstance.add(2, "there are virtual machines running in this pod");
tablesToCheck.add(vmInstance);
List<String> cluster = new ArrayList<String>();
cluster.add(0, "cluster");
cluster.add(1, "pod_id");
cluster.add(2, "there are clusters in this pod");
tablesToCheck.add(cluster);
for (List<String> table : tablesToCheck) {
String tableName = table.get(0);
String column = table.get(1);
String errorMsg = table.get(2);
String dbName;
if (tableName.equals("event") || tableName.equals("cloud_usage") || tableName.equals("usage_vm_instance") || tableName.equals("usage_ip_address") || tableName.equals("usage_network")
|| tableName.equals("usage_job") || tableName.equals("account") || tableName.equals("user_statistics")) {
dbName = "cloud_usage";
} else {
dbName = "cloud";
}
String selectSql = "SELECT * FROM `" + dbName + "`.`" + tableName + "` WHERE " + column + " = ?";
if (tableName.equals("host") || tableName.equals("cluster") || tableName.equals("volumes") || tableName.equals("vm_instance")) {
selectSql += " and removed IS NULL";
}
Transaction txn = Transaction.currentTxn();
try {
PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql);
stmt.setLong(1, podId);
ResultSet rs = stmt.executeQuery();
if (rs != null && rs.next()) {
throw new CloudRuntimeException("The pod cannot be deleted because " + errorMsg);
}
} catch (SQLException ex) {
throw new CloudRuntimeException("The Management Server failed to detect if pod is deletable. Please contact Cloud Support.");
}
}
}
private void checkPodAttributes(long podId, String podName, long zoneId, String gateway, String cidr, String startIp, String endIp, String allocationStateStr, boolean checkForDuplicates, boolean skipGatewayOverlapCheck) {
if (checkForDuplicates) {
// Check if the pod already exists
if (validPod(podName, zoneId)) {
throw new InvalidParameterValueException("A pod with name: " + podName + " already exists in zone " + zoneId + ". Please specify a different pod name. ");
}
}
String cidrAddress;
long cidrSize;
// Get the individual cidrAddress and cidrSize values, if the CIDR is valid. If it's not valid, return an error.
if (NetUtils.isValidCIDR(cidr)) {
cidrAddress = getCidrAddress(cidr);
cidrSize = getCidrSize(cidr);
} else {
throw new InvalidParameterValueException("Please enter a valid CIDR for pod: " + podName);
}
// Check if the IP range is valid
if (startIp != null || endIp != null) {
checkIpRange(startIp, endIp, cidrAddress, cidrSize);
}
// Check if the gateway is a valid IP address
if (!NetUtils.isValidIp(gateway)) {
throw new InvalidParameterValueException("The gateway is not a valid IP address.");
}
// Check if the gateway is in the CIDR subnet
if (!NetUtils.getCidrSubNet(gateway, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
throw new InvalidParameterValueException("The gateway is not in the CIDR subnet.");
}
// Don't allow gateway to overlap with start/endIp
if(!skipGatewayOverlapCheck){
if (NetUtils.ipRangesOverlap(startIp, endIp, gateway, gateway)) {
throw new InvalidParameterValueException("The gateway shouldn't overlap start/end ip addresses");
}
}
String checkPodCIDRs = _configDao.getValue("check.pod.cidrs");
if (checkPodCIDRs == null || checkPodCIDRs.trim().isEmpty() || Boolean.parseBoolean(checkPodCIDRs)) {
// Check if the CIDR conflicts with the Guest Network or other pods
HashMap<Long, List<Object>> currentPodCidrSubnets = _podDao.getCurrentPodCidrSubnets(zoneId, podId);
List<Object> newCidrPair = new ArrayList<Object>();
newCidrPair.add(0, cidrAddress);
newCidrPair.add(1, new Long(cidrSize));
currentPodCidrSubnets.put(new Long(-1), newCidrPair);
checkPodCidrSubnets(zoneId, currentPodCidrSubnets);
// Prevent using the same CIDR for POD and virtual networking
List<VlanVO> vlans = _vlanDao.listByZoneAndType(zoneId, VlanType.VirtualNetwork);
for (VlanVO vlan : vlans) {
String vlanCidr = NetUtils.ipAndNetMaskToCidr(vlan.getVlanGateway(), vlan.getVlanNetmask());
String[] cidrPairVlan = vlanCidr.split("\\/");
String[] vlanIpRange = NetUtils.getIpRangeFromCidr(cidrPairVlan[0], Long.valueOf(cidrPairVlan[1]));
String[] cidrPairPod = cidr.split("\\/");
String[] podIpRange = NetUtils.getIpRangeFromCidr(cidrPairPod[0], Long.valueOf(cidrPairPod[1]));
if (NetUtils.ipRangesOverlap(vlanIpRange[0], vlanIpRange[1], podIpRange[0], podIpRange[1])) {
throw new InvalidParameterValueException("Pod's cidr conflicts with cidr of virtual network in zone id=" + zoneId);
}
}
}
Grouping.AllocationState allocationState = null;
if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
try {
allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException("Unable to resolve Allocation State '" + allocationStateStr + "' to a supported state");
}
}
}
@Override
@DB
public boolean deletePod(DeletePodCmd cmd) {
Long podId = cmd.getId();
// Make sure the pod exists
if (!validPod(podId)) {
throw new InvalidParameterValueException("A pod with ID: " + podId + " does not exist.");
}
checkIfPodIsDeletable(podId);
HostPodVO pod = _podDao.findById(podId);
// Delete private ip addresses for the pod if there are any
List<DataCenterIpAddressVO> privateIps = _privateIpAddressDao.listByPodIdDcId(Long.valueOf(podId), pod.getDataCenterId());
if (privateIps != null && privateIps.size() != 0) {
if (!(_privateIpAddressDao.deleteIpAddressByPod(podId))) {
throw new CloudRuntimeException("Failed to cleanup private ip addresses for pod " + podId);
}
}
// Delete link local ip addresses for the pod
List<DataCenterLinkLocalIpAddressVO> localIps = _LinkLocalIpAllocDao.listByPodIdDcId(podId, pod.getDataCenterId());
if (!localIps.isEmpty()) {
if (!(_LinkLocalIpAllocDao.deleteIpAddressByPod(podId))) {
throw new CloudRuntimeException("Failed to cleanup private ip addresses for pod " + podId);
}
}
// Delete vlans associated with the pod
List<? extends Vlan> vlans = _networkMgr.listPodVlans(podId);
if (vlans != null && !vlans.isEmpty()) {
for (Vlan vlan : vlans) {
_vlanDao.remove(vlan.getId());
}
}
// Delete the pod
if (!(_podDao.remove(podId))) {
throw new CloudRuntimeException("Failed to delete pod " + podId);
}
return true;
}
@Override
public Pod editPod(UpdatePodCmd cmd) {
return editPod(cmd.getId(), cmd.getPodName(), cmd.getStartIp(), cmd.getEndIp(), cmd.getGateway(), cmd.getNetmask(), cmd.getAllocationState());
}
@Override
@DB
public Pod editPod(long id, String name, String startIp, String endIp, String gateway, String netmask, String allocationStateStr) {
// verify parameters
HostPodVO pod = _podDao.findById(id);
;
if (pod == null) {
throw new InvalidParameterValueException("Unable to find pod by id " + id);
}
String[] existingPodIpRange = pod.getDescription().split("-");
String[] leftRangeToAdd = null;
String[] rightRangeToAdd = null;
boolean allowToDownsize = false;
// If the gateway, CIDR, private IP range is being changed, check if the pod has allocated private IP addresses
if (podHasAllocatedPrivateIPs(id)) {
if (netmask != null) {
long newCidr = NetUtils.getCidrSize(netmask);
long oldCidr = pod.getCidrSize();
if (newCidr > oldCidr) {
throw new CloudRuntimeException("The specified pod has allocated private IP addresses, so its IP address range can be extended only");
}
}
if (startIp != null && !startIp.equals(existingPodIpRange[0])) {
if (NetUtils.ipRangesOverlap(startIp, null, existingPodIpRange[0], existingPodIpRange[1])) {
throw new CloudRuntimeException("The specified pod has allocated private IP addresses, so its IP address range can be extended only");
} else {
leftRangeToAdd = new String[2];
long endIpForUpdate = NetUtils.ip2Long(existingPodIpRange[0]) - 1;
leftRangeToAdd[0] = startIp;
leftRangeToAdd[1] = NetUtils.long2Ip(endIpForUpdate);
}
}
if (endIp != null && !endIp.equals(existingPodIpRange[1])) {
if (NetUtils.ipRangesOverlap(endIp, endIp, existingPodIpRange[0], existingPodIpRange[1])) {
throw new CloudRuntimeException("The specified pod has allocated private IP addresses, so its IP address range can be extended only");
} else {
rightRangeToAdd = new String[2];
long startIpForUpdate = NetUtils.ip2Long(existingPodIpRange[1]) + 1;
rightRangeToAdd[0] = NetUtils.long2Ip(startIpForUpdate);
rightRangeToAdd[1] = endIp;
}
}
} else {
allowToDownsize = true;
}
if (gateway == null) {
gateway = pod.getGateway();
}
if (netmask == null) {
netmask = NetUtils.getCidrNetmask(pod.getCidrSize());
}
String oldPodName = pod.getName();
if (name == null) {
name = oldPodName;
}
if (gateway == null) {
gateway = pod.getGateway();
}
if (startIp == null) {
startIp = existingPodIpRange[0];
}
if (endIp == null) {
endIp = existingPodIpRange[1];
}
if (allocationStateStr == null) {
allocationStateStr = pod.getAllocationState().toString();
}
// Verify pod's attributes
String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
boolean checkForDuplicates = !oldPodName.equals(name);
checkPodAttributes(id, name, pod.getDataCenterId(), gateway, cidr, startIp, endIp, allocationStateStr, checkForDuplicates, false);
Transaction txn = Transaction.currentTxn();
try {
txn.start();
long zoneId = pod.getDataCenterId();
if (!allowToDownsize) {
if (leftRangeToAdd != null) {
_zoneDao.addPrivateIpAddress(zoneId, pod.getId(), leftRangeToAdd[0], leftRangeToAdd[1]);
}
if (rightRangeToAdd != null) {
_zoneDao.addPrivateIpAddress(zoneId, pod.getId(), rightRangeToAdd[0], rightRangeToAdd[1]);
}
} else {
// delete the old range
_zoneDao.deletePrivateIpAddressByPod(pod.getId());
// add the new one
if (startIp == null) {
startIp = existingPodIpRange[0];
}
if (endIp == null) {
endIp = existingPodIpRange[1];
}
_zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIp);
}
pod.setName(name);
pod.setDataCenterId(zoneId);
pod.setGateway(gateway);
pod.setCidrAddress(getCidrAddress(cidr));
pod.setCidrSize(getCidrSize(cidr));
String ipRange = startIp + "-" + endIp;
pod.setDescription(ipRange);
Grouping.AllocationState allocationState = null;
if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
pod.setAllocationState(allocationState);
}
_podDao.update(id, pod);
txn.commit();
} catch (Exception e) {
s_logger.error("Unable to edit pod due to " + e.getMessage(), e);
throw new CloudRuntimeException("Failed to edit pod. Please contact Cloud Support.");
}
return pod;
}
@Override
public Pod createPod(CreatePodCmd cmd) {
String endIp = cmd.getEndIp();
String gateway = cmd.getGateway();
String name = cmd.getPodName();
String startIp = cmd.getStartIp();
String netmask = cmd.getNetmask();
Long zoneId = cmd.getZoneId();
String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
Long userId = UserContext.current().getCallerUserId();
String allocationState = cmd.getAllocationState();
if (allocationState == null) {
allocationState = Grouping.AllocationState.Enabled.toString();
}
return createPod(userId.longValue(), name, zoneId, gateway, cidr, startIp, endIp, allocationState, false);
}
@Override
@DB
public HostPodVO createPod(long userId, String podName, long zoneId, String gateway, String cidr, String startIp, String endIp, String allocationStateStr, boolean skipGatewayOverlapCheck) {
// Check if the zone is valid
if (!validZone(zoneId)) {
throw new InvalidParameterValueException("Please specify a valid zone.");
}
// Check if zone is disabled
DataCenterVO zone = _zoneDao.findById(zoneId);
Account account = UserContext.current().getCaller();
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(account.getType())) {
throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
}
String cidrAddress = getCidrAddress(cidr);
int cidrSize = getCidrSize(cidr);
// endIp is an optional parameter; if not specified - default it to the end ip of the pod's cidr
if (startIp != null) {
if (endIp == null) {
endIp = NetUtils.getIpRangeEndIpFromCidr(cidrAddress, cidrSize);
}
}
// Validate new pod settings
checkPodAttributes(-1, podName, zoneId, gateway, cidr, startIp, endIp, allocationStateStr, true, skipGatewayOverlapCheck);
// Create the new pod in the database
String ipRange;
if (startIp != null) {
ipRange = startIp + "-" + endIp;
} else {
throw new InvalidParameterValueException("Start ip is required parameter");
}
HostPodVO pod = new HostPodVO(podName, zoneId, gateway, cidrAddress, cidrSize, ipRange);
Grouping.AllocationState allocationState = null;
if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
pod.setAllocationState(allocationState);
}
Transaction txn = Transaction.currentTxn();
txn.start();
pod = _podDao.persist(pod);
if (startIp != null) {
_zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIp);
}
String[] linkLocalIpRanges = getLinkLocalIPRange();
if (linkLocalIpRanges != null) {
_zoneDao.addLinkLocalIpAddress(zoneId, pod.getId(), linkLocalIpRanges[0], linkLocalIpRanges[1]);
}
txn.commit();
return pod;
}
private boolean zoneHasVMs(long zoneId) {
List<VMInstanceVO> vmInstances = _vmInstanceDao.listByZoneId(zoneId);
return !vmInstances.isEmpty();
}
private boolean zoneHasAllocatedVnets(long zoneId) {
return !_zoneDao.listAllocatedVnets(zoneId).isEmpty();
}
@DB
protected void checkIfZoneIsDeletable(long zoneId) {
List<List<String>> tablesToCheck = new ArrayList<List<String>>();
List<String> host = new ArrayList<String>();
host.add(0, "host");
host.add(1, "data_center_id");
host.add(2, "there are servers running in this zone");
tablesToCheck.add(host);
List<String> hostPodRef = new ArrayList<String>();
hostPodRef.add(0, "host_pod_ref");
hostPodRef.add(1, "data_center_id");
hostPodRef.add(2, "there are pods in this zone");
tablesToCheck.add(hostPodRef);
List<String> privateIP = new ArrayList<String>();
privateIP.add(0, "op_dc_ip_address_alloc");
privateIP.add(1, "data_center_id");
privateIP.add(2, "there are private IP addresses allocated for this zone");
tablesToCheck.add(privateIP);
List<String> publicIP = new ArrayList<String>();
publicIP.add(0, "user_ip_address");
publicIP.add(1, "data_center_id");
publicIP.add(2, "there are public IP addresses allocated for this zone");
tablesToCheck.add(publicIP);
List<String> vmInstance = new ArrayList<String>();
vmInstance.add(0, "vm_instance");
vmInstance.add(1, "data_center_id");
vmInstance.add(2, "there are virtual machines running in this zone");
tablesToCheck.add(vmInstance);
List<String> volumes = new ArrayList<String>();
volumes.add(0, "volumes");
volumes.add(1, "data_center_id");
volumes.add(2, "there are storage volumes for this zone");
tablesToCheck.add(volumes);
List<String> vnet = new ArrayList<String>();
vnet.add(0, "op_dc_vnet_alloc");
vnet.add(1, "data_center_id");
vnet.add(2, "there are allocated vnets for this zone");
tablesToCheck.add(vnet);
for (List<String> table : tablesToCheck) {
String tableName = table.get(0);
String column = table.get(1);
String errorMsg = table.get(2);
String dbName = "cloud";
String selectSql = "SELECT * FROM `" + dbName + "`.`" + tableName + "` WHERE " + column + " = ?";
if (tableName.equals("op_dc_vnet_alloc")) {
selectSql += " AND taken IS NOT NULL";
}
if (tableName.equals("user_ip_address")) {
selectSql += " AND state!='Free'";
}
if (tableName.equals("op_dc_ip_address_alloc")) {
selectSql += " AND taken IS NOT NULL";
}
if (tableName.equals("host_pod_ref") || tableName.equals("host") || tableName.equals("volumes")) {
selectSql += " AND removed is NULL";
}
if (tableName.equals("vm_instance")) {
selectSql += " AND state != '" + VirtualMachine.State.Expunging.toString() + "'";
}
Transaction txn = Transaction.currentTxn();
try {
PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql);
stmt.setLong(1, zoneId);
ResultSet rs = stmt.executeQuery();
if (rs != null && rs.next()) {
throw new CloudRuntimeException("The zone is not deletable because " + errorMsg);
}
} catch (SQLException ex) {
throw new CloudRuntimeException("The Management Server failed to detect if zone is deletable. Please contact Cloud Support.");
}
}
}
private void checkZoneParameters(String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, boolean checkForDuplicates, Long domainId, String allocationStateStr) {
if (checkForDuplicates) {
// Check if a zone with the specified name already exists
if (validZone(zoneName)) {
throw new InvalidParameterValueException("A zone with that name already exists. Please specify a unique zone name.");
}
}
// check if valid domain
if (domainId != null) {
DomainVO domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Please specify a valid domain id");
}
}
// Check IP validity for DNS addresses
// Empty strings is a valid input -- hence the length check
if (dns1 != null && dns1.length() > 0 && !NetUtils.isValidIp(dns1)) {
throw new InvalidParameterValueException("Please enter a valid IP address for DNS1");
}
if (dns2 != null && dns2.length() > 0 && !NetUtils.isValidIp(dns2)) {
throw new InvalidParameterValueException("Please enter a valid IP address for DNS2");
}
if ((internalDns1 != null && internalDns1.length() > 0 && !NetUtils.isValidIp(internalDns1))) {
throw new InvalidParameterValueException("Please enter a valid IP address for internal DNS1");
}
if (internalDns2 != null && internalDns2.length() > 0 && !NetUtils.isValidIp(internalDns2)) {
throw new InvalidParameterValueException("Please enter a valid IP address for internal DNS2");
}
Grouping.AllocationState allocationState = null;
if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
try {
allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException("Unable to resolve Allocation State '" + allocationStateStr + "' to a supported state");
}
}
}
private void checkIpRange(String startIp, String endIp, String cidrAddress, long cidrSize) {
if (!NetUtils.isValidIp(startIp)) {
throw new InvalidParameterValueException("The start address of the IP range is not a valid IP address.");
}
if (endIp != null && !NetUtils.isValidIp(endIp)) {
throw new InvalidParameterValueException("The end address of the IP range is not a valid IP address.");
}
if (!NetUtils.getCidrSubNet(startIp, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
throw new InvalidParameterValueException("The start address of the IP range is not in the CIDR subnet.");
}
if (endIp != null && !NetUtils.getCidrSubNet(endIp, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
throw new InvalidParameterValueException("The end address of the IP range is not in the CIDR subnet.");
}
if (endIp != null && NetUtils.ip2Long(startIp) > NetUtils.ip2Long(endIp)) {
throw new InvalidParameterValueException("The start IP address must have a lower value than the end IP address.");
}
}
@Override
@DB
public boolean deleteZone(DeleteZoneCmd cmd) {
Transaction txn = Transaction.currentTxn();
boolean success = false;
Long userId = UserContext.current().getCallerUserId();
Long zoneId = cmd.getId();
if (userId == null) {
userId = Long.valueOf(User.UID_SYSTEM);
}
// Make sure the zone exists
if (!validZone(zoneId)) {
throw new InvalidParameterValueException("A zone with ID: " + zoneId + " does not exist.");
}
checkIfZoneIsDeletable(zoneId);
txn.start();
// Delete vNet
_zoneDao.deleteVnet(zoneId);
// delete vlans for this zone
List<VlanVO> vlans = _vlanDao.listByZone(zoneId);
for (VlanVO vlan : vlans) {
_vlanDao.remove(vlan.getId());
}
// Delete networks
List<NetworkVO> networks = _networkDao.listByZoneIncludingRemoved(zoneId);
if (networks != null && !networks.isEmpty()) {
for (NetworkVO network : networks) {
_networkDao.remove(network.getId());
}
}
success = _zoneDao.remove(zoneId);
txn.commit();
return success;
}
@Override
public DataCenter editZone(UpdateZoneCmd cmd) {
// Parameter validation as from execute() method in V1
Long zoneId = cmd.getId();
String zoneName = cmd.getZoneName();
String dns1 = cmd.getDns1();
String dns2 = cmd.getDns2();
String internalDns1 = cmd.getInternalDns1();
String internalDns2 = cmd.getInternalDns2();
String vnetRange = cmd.getVlan();
String guestCidr = cmd.getGuestCidrAddress();
Long userId = UserContext.current().getCallerUserId();
int startVnetRange = 0;
int stopVnetRange = 0;
Boolean isPublic = cmd.isPublic();
String allocationStateStr = cmd.getAllocationState();
String dhcpProvider = cmd.getDhcpProvider();
Map detailsMap = cmd.getDetails();
Map<String, String> newDetails = new HashMap<String, String>();
if (detailsMap != null) {
Collection zoneDetailsCollection = detailsMap.values();
Iterator iter = zoneDetailsCollection.iterator();
while (iter.hasNext()) {
HashMap detail = (HashMap)iter.next();
String key = (String)detail.get("key");
String value = (String)detail.get("value");
if ((key == null) || (value == null)) {
throw new InvalidParameterValueException("Invalid Zone Detail specified, fields 'key' and 'value' cannot be null, please specify details in the form: details[0].key=XXX&details[0].value=YYY");
}
//validate the zone detail keys are known keys
/*if(!ZoneConfig.doesKeyExist(key)){
throw new InvalidParameterValueException("Invalid Zone Detail parameter: "+ key);
}*/
newDetails.put(key, value);
}
}
if (userId == null) {
userId = Long.valueOf(User.UID_SYSTEM);
}
DataCenterVO zone = _zoneDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("unable to find zone by id " + zoneId);
}
if (zoneName == null) {
zoneName = zone.getName();
}
// if zone is of Basic type, don't allow to add vnet range and cidr
if (zone.getNetworkType() == NetworkType.Basic) {
if (vnetRange != null) {
throw new InvalidParameterValueException("Can't add vnet range for the zone that supports " + zone.getNetworkType() + " network");
} else if (guestCidr != null) {
throw new InvalidParameterValueException("Can't add cidr for the zone that supports " + zone.getNetworkType() + " network");
}
}
if ((guestCidr != null) && !NetUtils.validateGuestCidr(guestCidr)) {
throw new InvalidParameterValueException("Please enter a valid guest cidr");
}
// Make sure the zone exists
if (!validZone(zoneId)) {
throw new InvalidParameterValueException("A zone with ID: " + zoneId + " does not exist.");
}
// If the Vnet range is being changed, make sure there are no allocated VNets
if (vnetRange != null) {
if (zoneHasAllocatedVnets(zoneId)) {
throw new CloudRuntimeException("The vlan range is not editable because there are allocated vlans.");
}
String[] startStopRange = new String[2];
startStopRange = vnetRange.split("-");
if (startStopRange.length == 1) {
throw new InvalidParameterValueException("Please provide valid vnet range between 0-4096");
}
if (startStopRange[0] == null || startStopRange[1] == null) {
throw new InvalidParameterValueException("Please provide valid vnet range between 0-4096");
}
try {
startVnetRange = Integer.parseInt(startStopRange[0]);
stopVnetRange = Integer.parseInt(startStopRange[1]);
} catch (NumberFormatException e) {
s_logger.warn("Unable to parse vnet range:", e);
throw new InvalidParameterValueException("Please provide valid vnet range between 0-4096");
}
if (startVnetRange < 0 || stopVnetRange > 4096) {
throw new InvalidParameterValueException("Vnet range has to be between 0-4096");
}
if (startVnetRange > stopVnetRange) {
throw new InvalidParameterValueException("Vnet range has to be between 0-4096 and start range should be lesser than or equal to stop range");
}
}
String oldZoneName = zone.getName();
if (zoneName == null) {
zoneName = oldZoneName;
}
boolean dnsUpdate = false;
if (dns1 != null || dns2 != null) {
dnsUpdate = true;
}
if (dns1 == null) {
dns1 = zone.getDns1();
}
if (dns2 == null) {
dns2 = zone.getDns2();
}
if (internalDns1 == null) {
internalDns1 = zone.getInternalDns1();
}
if (guestCidr == null) {
guestCidr = zone.getGuestNetworkCidr();
}
boolean checkForDuplicates = !zoneName.equals(oldZoneName);
checkZoneParameters(zoneName, dns1, dns2, internalDns1, internalDns2, checkForDuplicates, null, allocationStateStr);// not
// allowing
// updating
// domain
// associated
// with
// a
// zone,
// once
// created
zone.setName(zoneName);
zone.setDns1(dns1);
zone.setDns2(dns2);
zone.setInternalDns1(internalDns1);
zone.setInternalDns2(internalDns2);
zone.setGuestNetworkCidr(guestCidr);
if (vnetRange != null) {
zone.setVnet(vnetRange);
}
// update a private zone to public; not vice versa
if (isPublic != null && isPublic) {
zone.setDomainId(null);
zone.setDomain(null);
}
Map<String, String> updatedDetails = new HashMap<String, String>();
_zoneDao.loadDetails(zone);
if(zone.getDetails() != null){
updatedDetails.putAll(zone.getDetails());
}
updatedDetails.putAll(newDetails);
zone.setDetails(updatedDetails);
if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
zone.setAllocationState(allocationState);
}
if(dhcpProvider != null){
zone.setDhcpProvider(dhcpProvider);
}
if (!_zoneDao.update(zoneId, zone)) {
throw new CloudRuntimeException("Failed to edit zone. Please contact Cloud Support.");
}
if (vnetRange != null) {
String[] tokens = vnetRange.split("-");
int begin = Integer.parseInt(tokens[0]);
int end = tokens.length == 1 ? (begin) : Integer.parseInt(tokens[1]);
_zoneDao.deleteVnet(zoneId);
_zoneDao.addVnet(zone.getId(), begin, end);
}
return zone;
}
@Override
@DB
public DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String vnetRange, String guestCidr, String domain, Long domainId,
NetworkType zoneType, boolean isSecurityGroupEnabled, String allocationStateStr) {
int vnetStart = 0;
int vnetEnd = 0;
if (vnetRange != null) {
String[] tokens = vnetRange.split("-");
try {
vnetStart = Integer.parseInt(tokens[0]);
if (tokens.length == 1) {
vnetEnd = vnetStart;
} else {
vnetEnd = Integer.parseInt(tokens[1]);
}
} catch (NumberFormatException e) {
throw new InvalidParameterValueException("Please specify valid integers for the vlan range.");
}
if ((vnetStart > vnetEnd) || (vnetStart < 0) || (vnetEnd > 4096)) {
s_logger.warn("Invalid vnet range: start range:" + vnetStart + " end range:" + vnetEnd);
throw new InvalidParameterValueException("Vnet range should be between 0-4096 and start range should be lesser than or equal to end range");
}
}
// checking the following params outside checkzoneparams method as we do not use these params for updatezone
// hence the method below is generic to check for common params
if ((guestCidr != null) && !NetUtils.validateGuestCidr(guestCidr)) {
throw new InvalidParameterValueException("Please enter a valid guest cidr");
}
checkZoneParameters(zoneName, dns1, dns2, internalDns1, internalDns2, true, domainId, allocationStateStr);
byte[] bytes = (zoneName + System.currentTimeMillis()).getBytes();
String zoneToken = UUID.nameUUIDFromBytes(bytes).toString();
Transaction txn = Transaction.currentTxn();
try {
txn.start();
// Create the new zone in the database
DataCenterVO zone = new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, vnetRange, guestCidr, domain, domainId, zoneType, isSecurityGroupEnabled, zoneToken);
if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
zone.setAllocationState(allocationState);
}
zone = _zoneDao.persist(zone);
// Add vnet entries for the new zone if zone type is Advanced
if (vnetRange != null) {
_zoneDao.addVnet(zone.getId(), vnetStart, vnetEnd);
}
// Create deafult networks
createDefaultNetworks(zone.getId(), isSecurityGroupEnabled);
txn.commit();
return zone;
} catch (Exception ex) {
txn.rollback();
s_logger.warn("Exception: ", ex);
throw new CloudRuntimeException("Fail to create a network");
} finally {
txn.close();
}
}
@Override
public void createDefaultNetworks(long zoneId, boolean isSecurityGroupEnabled) throws ConcurrentOperationException {
DataCenterVO zone = _zoneDao.findById(zoneId);
// Create public, management, control and storage networks as a part of the zone creation
if (zone != null) {
List<NetworkOfferingVO> ntwkOff = _networkOfferingDao.listSystemNetworkOfferings();
for (NetworkOfferingVO offering : ntwkOff) {
DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, null, null);
NetworkVO userNetwork = new NetworkVO();
Account systemAccount = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM);
BroadcastDomainType broadcastDomainType = null;
boolean isNetworkDefault = false;
if (offering.getTrafficType() == TrafficType.Management) {
broadcastDomainType = BroadcastDomainType.Native;
} else if (offering.getTrafficType() == TrafficType.Control) {
broadcastDomainType = BroadcastDomainType.LinkLocal;
} else if (offering.getTrafficType() == TrafficType.Public) {
if ((zone.getNetworkType() == NetworkType.Advanced && !zone.isSecurityGroupEnabled()) || zone.getNetworkType() == NetworkType.Basic) {
broadcastDomainType = BroadcastDomainType.Vlan;
} else {
continue;
}
} else if (offering.getTrafficType() == TrafficType.Guest) {
if (zone.getNetworkType() == NetworkType.Basic) {
isNetworkDefault = true;
broadcastDomainType = BroadcastDomainType.Native;
userNetwork.setSecurityGroupEnabled(isSecurityGroupEnabled);
} else if (offering.getGuestType() == GuestIpType.Direct && isSecurityGroupEnabled) {
isNetworkDefault = true;
userNetwork.setSecurityGroupEnabled(isSecurityGroupEnabled);
} else {
continue;
}
}
userNetwork.setBroadcastDomainType(broadcastDomainType);
_networkMgr.setupNetwork(systemAccount, offering, userNetwork, plan, null, null, true, isNetworkDefault, false, null, null);
}
}
}
@Override
public DataCenter createZone(CreateZoneCmd cmd) {
// grab parameters from the command
Long userId = UserContext.current().getCallerUserId();
String zoneName = cmd.getZoneName();
String dns1 = cmd.getDns1();
String dns2 = cmd.getDns2();
String internalDns1 = cmd.getInternalDns1();
String internalDns2 = cmd.getInternalDns2();
String vnetRange = cmd.getVlan();
String guestCidr = cmd.getGuestCidrAddress();
Long domainId = cmd.getDomainId();
String type = cmd.getNetworkType();
Boolean isBasic = false;
String allocationState = cmd.getAllocationState();
if (allocationState == null) {
allocationState = Grouping.AllocationState.Enabled.toString();
}
if (!(type.equalsIgnoreCase(NetworkType.Basic.toString())) && !(type.equalsIgnoreCase(NetworkType.Advanced.toString()))) {
throw new InvalidParameterValueException("Invalid zone type; only Advanced and Basic values are supported");
} else if (type.equalsIgnoreCase(NetworkType.Basic.toString())) {
isBasic = true;
}
Boolean securityGroupEnabled = cmd.isSecurityGroupEnabled();
NetworkType zoneType = isBasic ? NetworkType.Basic : NetworkType.Advanced;
// Guest cidr is required for Advanced zone creation; error out when the parameter specified for Basic zone
if (zoneType == NetworkType.Advanced && guestCidr == null && !securityGroupEnabled) {
throw new InvalidParameterValueException("guestCidrAddress parameter is required for Advanced zone creation");
} else if (zoneType == NetworkType.Basic && guestCidr != null) {
throw new InvalidParameterValueException("guestCidrAddress parameter is not supported for Basic zone");
}
DomainVO domainVO = null;
if (userId == null) {
userId = User.UID_SYSTEM;
}
if (domainId != null) {
domainVO = _domainDao.findById(domainId);
}
// Verify zone type
if (zoneType == NetworkType.Basic && vnetRange != null) {
vnetRange = null;
}
if (zoneType == NetworkType.Basic) {
securityGroupEnabled = true;
}
return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, vnetRange, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, securityGroupEnabled,
allocationState);
}
@Override
public ServiceOffering createServiceOffering(CreateServiceOfferingCmd cmd) {
Long userId = UserContext.current().getCallerUserId();
if (userId == null) {
userId = User.UID_SYSTEM;
}
String name = cmd.getServiceOfferingName();
if ((name == null) || (name.length() == 0)) {
throw new InvalidParameterValueException("Failed to create service offering: specify the name that has non-zero length");
}
String displayText = cmd.getDisplayText();
if ((displayText == null) || (displayText.length() == 0)) {
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the display text that has non-zero length");
}
Long cpuNumber = cmd.getCpuNumber();
if ((cpuNumber == null) || (cpuNumber.intValue() <= 0) || (cpuNumber.intValue() > 2147483647)) {
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu number value between 1 and 2147483647");
}
Long cpuSpeed = cmd.getCpuSpeed();
if ((cpuSpeed == null) || (cpuSpeed.intValue() <= 0) || (cpuSpeed.intValue() > 2147483647)) {
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu speed value between 1 and 2147483647");
}
Long memory = cmd.getMemory();
if ((memory == null) || (memory.intValue() <= 0) || (memory.intValue() > 2147483647)) {
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the memory value between 1 and 2147483647");
}
// check if valid domain
if (cmd.getDomainId() != null) {
DomainVO domain = _domainDao.findById(cmd.getDomainId());
if (domain == null) {
throw new InvalidParameterValueException("Please specify a valid domain id");
}
}
boolean localStorageRequired = false;
String storageType = cmd.getStorageType();
if (storageType == null) {
localStorageRequired = false;
} else if (storageType.equals("local")) {
localStorageRequired = true;
} else if (storageType.equals("shared")) {
localStorageRequired = false;
} else {
throw new InvalidParameterValueException("Invalid storage type " + storageType + " specified, valid types are: 'local' and 'shared'");
}
Boolean offerHA = cmd.getOfferHa();
if (offerHA == null) {
offerHA = false;
}
Boolean limitCpuUse = cmd.GetLimitCpuUse();
if (limitCpuUse == null) {
limitCpuUse = false;
}
return createServiceOffering(userId, cmd.getIsSystem(), cmd.getServiceOfferingName(), cpuNumber.intValue(), memory.intValue(), cpuSpeed.intValue(), cmd.getDisplayText(), localStorageRequired, offerHA,
limitCpuUse, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag());
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_CREATE, eventDescription = "creating service offering")
public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, String tags,
Long domainId, String hostTag) {
tags = cleanupTags(tags);
ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, offerHA, limitResourceUse, displayText, localStorageRequired, false, tags, isSystem, domainId, hostTag);
if ((offering = _serviceOfferingDao.persist(offering)) != null) {
UserContext.current().setEventDetails("Service offering id=" + offering.getId());
return offering;
} else {
return null;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_EDIT, eventDescription = "updating service offering")
public ServiceOffering updateServiceOffering(UpdateServiceOfferingCmd cmd) {
String displayText = cmd.getDisplayText();
Long id = cmd.getId();
String name = cmd.getServiceOfferingName();
Long userId = UserContext.current().getCallerUserId();
if (userId == null) {
userId = Long.valueOf(User.UID_SYSTEM);
}
// Verify input parameters
ServiceOfferingVO offeringHandle = _serviceOfferingDao.findById(id);
;
if (offeringHandle == null) {
throw new InvalidParameterValueException("unable to find service offering " + id);
}
boolean updateNeeded = (name != null || displayText != null);
if (!updateNeeded) {
return _serviceOfferingDao.findById(id);
}
ServiceOfferingVO offering = _serviceOfferingDao.createForUpdate(id);
if (name != null) {
offering.setName(name);
}
if (displayText != null) {
offering.setDisplayText(displayText);
}
// Note: tag editing commented out for now; keeping the code intact, might need to re-enable in next releases
// if (tags != null)
// {
// if (tags.trim().isEmpty() && offeringHandle.getTags() == null)
// {
// //no new tags; no existing tags
// offering.setTagsArray(csvTagsToList(null));
// }
// else if (!tags.trim().isEmpty() && offeringHandle.getTags() != null)
// {
// //new tags + existing tags
// List<String> oldTags = csvTagsToList(offeringHandle.getTags());
// List<String> newTags = csvTagsToList(tags);
// oldTags.addAll(newTags);
// offering.setTagsArray(oldTags);
// }
// else if(!tags.trim().isEmpty())
// {
// //new tags; NO existing tags
// offering.setTagsArray(csvTagsToList(tags));
// }
// }
if (_serviceOfferingDao.update(id, offering)) {
offering = _serviceOfferingDao.findById(id);
UserContext.current().setEventDetails("Service offering id=" + offering.getId());
return offering;
} else {
return null;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_CREATE, eventDescription = "creating disk offering")
public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized) {
long diskSize = 0;// special case for custom disk offerings
if (numGibibytes != null && (numGibibytes <= 0)) {
throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb.");
} else if (numGibibytes != null && (numGibibytes > _maxVolumeSizeInGb)) {
throw new InvalidParameterValueException("The maximum size for a disk is " + _maxVolumeSizeInGb + " Gb.");
}
if (numGibibytes != null) {
diskSize = numGibibytes * 1024 * 1024 * 1024;
}
if (diskSize == 0) {
isCustomized = true;
}
tags = cleanupTags(tags);
DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized);
UserContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering);
if (offering != null) {
UserContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
return offering;
} else {
return null;
}
}
@Override
public DiskOffering createDiskOffering(CreateDiskOfferingCmd cmd) {
String name = cmd.getOfferingName();
String description = cmd.getDisplayText();
Long numGibibytes = cmd.getDiskSize();
boolean isCustomized = cmd.isCustomized() != null ? cmd.isCustomized() : false; // false by default
String tags = cmd.getTags();
// Long domainId = cmd.getDomainId() != null ? cmd.getDomainId() : Long.valueOf(DomainVO.ROOT_DOMAIN); // disk offering
// always gets created under the root domain.Bug # 6055 if not passed in cmd
Long domainId = cmd.getDomainId();
if (!isCustomized && numGibibytes == null) {
throw new InvalidParameterValueException("Disksize is required for non-customized disk offering");
}
return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized);
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_EDIT, eventDescription = "updating disk offering")
public DiskOffering updateDiskOffering(UpdateDiskOfferingCmd cmd) {
Long diskOfferingId = cmd.getId();
String name = cmd.getDiskOfferingName();
String displayText = cmd.getDisplayText();
// Check if diskOffering exists
DiskOfferingVO diskOfferingHandle = _diskOfferingDao.findById(diskOfferingId);
if (diskOfferingHandle == null) {
throw new InvalidParameterValueException("Unable to find disk offering by id " + diskOfferingId);
}
boolean updateNeeded = (name != null || displayText != null);
if (!updateNeeded) {
return _diskOfferingDao.findById(diskOfferingId);
}
DiskOfferingVO diskOffering = _diskOfferingDao.createForUpdate(diskOfferingId);
if (name != null) {
diskOffering.setName(name);
}
if (displayText != null) {
diskOffering.setDisplayText(displayText);
}
// Note: tag editing commented out for now;keeping the code intact, might need to re-enable in next releases
// if (tags != null)
// {
// if (tags.trim().isEmpty() && diskOfferingHandle.getTags() == null)
// {
// //no new tags; no existing tags
// diskOffering.setTagsArray(csvTagsToList(null));
// }
// else if (!tags.trim().isEmpty() && diskOfferingHandle.getTags() != null)
// {
// //new tags + existing tags
// List<String> oldTags = csvTagsToList(diskOfferingHandle.getTags());
// List<String> newTags = csvTagsToList(tags);
// oldTags.addAll(newTags);
// diskOffering.setTagsArray(oldTags);
// }
// else if(!tags.trim().isEmpty())
// {
// //new tags; NO existing tags
// diskOffering.setTagsArray(csvTagsToList(tags));
// }
// }
if (_diskOfferingDao.update(diskOfferingId, diskOffering)) {
UserContext.current().setEventDetails("Disk offering id=" + diskOffering.getId());
return _diskOfferingDao.findById(diskOfferingId);
} else {
return null;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_DELETE, eventDescription = "deleting disk offering")
public boolean deleteDiskOffering(DeleteDiskOfferingCmd cmd) {
Long diskOfferingId = cmd.getId();
DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId);
if (offering == null) {
throw new InvalidParameterValueException("Unable to find disk offering by id " + diskOfferingId);
}
if (_diskOfferingDao.remove(diskOfferingId)) {
UserContext.current().setEventDetails("Disk offering id=" + diskOfferingId);
return true;
} else {
return false;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_DELETE, eventDescription = "deleting service offering")
public boolean deleteServiceOffering(DeleteServiceOfferingCmd cmd) {
Long offeringId = cmd.getId();
Long userId = UserContext.current().getCallerUserId();
if (userId == null) {
userId = Long.valueOf(User.UID_SYSTEM);
}
// Verify service offering id
ServiceOfferingVO offering = _serviceOfferingDao.findById(offeringId);
if (offering == null) {
throw new InvalidParameterValueException("unable to find service offering " + offeringId);
} else if (offering.getRemoved() != null) {
throw new InvalidParameterValueException("unable to find service offering " + offeringId);
}
if (_serviceOfferingDao.remove(offeringId)) {
UserContext.current().setEventDetails("Service offering id=" + offeringId);
return true;
} else {
return false;
}
}
@Override
public String changePrivateIPRange(boolean add, long podId, String startIP, String endIP) {
checkPrivateIpRangeErrors(podId, startIP, endIP);
long zoneId = _podDao.findById(podId).getDataCenterId();
List<String> problemIPs = null;
if (add) {
problemIPs = savePrivateIPRange(startIP, endIP, podId, zoneId);
} else {
problemIPs = deletePrivateIPRange(startIP, endIP, podId, zoneId);
}
if (problemIPs == null) {
throw new InvalidParameterValueException("Failed to change private IP range. Please contact Cloud Support.");
} else {
return genChangeRangeSuccessString(problemIPs, add);
}
}
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_CREATE, eventDescription = "creating vlan ip range", async = false)
public Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
Long zoneId = cmd.getZoneId();
Long podId = cmd.getPodId();
String startIP = cmd.getStartIp();
String endIP = cmd.getEndIp();
String vlanGateway = cmd.getGateway();
String vlanNetmask = cmd.getNetmask();
Long userId = UserContext.current().getCallerUserId();
String vlanId = cmd.getVlan();
Boolean forVirtualNetwork = cmd.isForVirtualNetwork();
Long networkId = cmd.getNetworkID();
String networkVlanId = null;
// If an account name and domain ID are specified, look up the account
String accountName = cmd.getAccountName();
Long domainId = cmd.getDomainId();
Account account = null;
if ((accountName != null) && (domainId != null)) {
account = _accountDao.findActiveAccount(accountName, domainId);
if (account == null) {
throw new InvalidParameterValueException("Please specify a valid account.");
}
}
// Verify that network exists
Network network = null;
if (networkId != null) {
network = _networkDao.findById(networkId);
if (network == null) {
throw new InvalidParameterValueException("Unable to find network by id " + networkId);
} else {
zoneId = network.getDataCenterId();
}
}
// Verify that zone exists
DataCenterVO zone = _zoneDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
}
// Check if zone is disabled
Account caller = UserContext.current().getCaller();
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
}
if (zone.isSecurityGroupEnabled() && zone.getNetworkType() != DataCenter.NetworkType.Basic && forVirtualNetwork) {
throw new InvalidParameterValueException("Can't add virtual network into a zone with security group enabled");
}
// If networkId is not specified, and vlan is Virtual or Direct Untagged, try to locate default networks
if (forVirtualNetwork) {
if (network == null) {
// find default public network in the zone
networkId = _networkMgr.getSystemNetworkByZoneAndTrafficType(zoneId, TrafficType.Public).getId();
} else if (network.getGuestType() != null || network.getTrafficType() != TrafficType.Public) {
throw new InvalidParameterValueException("Can't find Public network by id=" + networkId);
}
} else {
if (network == null) {
if (zone.getNetworkType() == DataCenter.NetworkType.Basic) {
networkId = _networkMgr.getSystemNetworkByZoneAndTrafficType(zoneId, TrafficType.Guest).getId();
} else {
network = _networkMgr.getNetworkWithSecurityGroupEnabled(zoneId);
if (network == null) {
throw new InvalidParameterValueException("Nework id is required for Direct vlan creation ");
}
networkId = network.getId();
}
} else if (network.getGuestType() == null || network.getGuestType() == GuestIpType.Virtual) {
throw new InvalidParameterValueException("Can't create direct vlan for network id=" + networkId + " with GuestType: " + network.getGuestType());
}
}
// if end ip is not specified, default it to startIp
if (endIP == null && startIP != null) {
endIP = startIP;
}
// if vlan is specified, throw an error if it's not equal to network's vlanId
if (network != null) {
URI uri = network.getBroadcastUri();
if (uri != null) {
String[] vlan = uri.toString().split("vlan:\\/\\/");
networkVlanId = vlan[1];
}
}
if (vlanId != null && networkVlanId != null && !networkVlanId.equalsIgnoreCase(vlanId)) {
throw new InvalidParameterValueException("Vlan doesn't match vlan of the network");
}
if (forVirtualNetwork || zone.getNetworkType() == DataCenter.NetworkType.Basic || network.isSecurityGroupEnabled()) {
if (vlanGateway == null || vlanNetmask == null || zoneId == null) {
throw new InvalidParameterValueException("Gateway, netmask and zoneId have to be passed in for virtual and direct untagged networks");
}
} else {
// check if startIp and endIp belong to network Cidr
String networkCidr = network.getCidr();
String networkGateway = network.getGateway();
Long networkZoneId = network.getDataCenterId();
String networkNetmask = NetUtils.getCidrNetmask(networkCidr);
// Check if ip addresses are in network range
if (!NetUtils.sameSubnet(startIP, networkGateway, networkNetmask)) {
throw new InvalidParameterValueException("Start ip is not in network cidr: " + networkCidr);
}
if (endIP != null) {
if (!NetUtils.sameSubnet(endIP, networkGateway, networkNetmask)) {
throw new InvalidParameterValueException("End ip is not in network cidr: " + networkCidr);
}
}
// set gateway, netmask, zone from network object
vlanGateway = networkGateway;
vlanNetmask = networkNetmask;
zoneId = networkZoneId;
// set vlanId if it's not null for the network
if (networkVlanId != null) {
vlanId = networkVlanId;
}
}
// if it's an account specific range, associate ip address list to the account
boolean associateIpRangeToAccount = false;
if (forVirtualNetwork) {
if (account != null) {
// verify resource limits
long ipResourceLimit = _accountMgr.findCorrectResourceLimit((AccountVO) account, ResourceType.public_ip);
long accountIpRange = NetUtils.ip2Long(endIP) - NetUtils.ip2Long(startIP) + 1;
if (s_logger.isDebugEnabled()) {
s_logger.debug(" IPResourceLimit " + ipResourceLimit + " accountIpRange " + accountIpRange);
}
if (ipResourceLimit != -1 && accountIpRange > ipResourceLimit) { // -1 means infinite
throw new InvalidParameterValueException(" Public IP Resource Limit is set to " + ipResourceLimit + " which is less than the IP range of " + accountIpRange + " provided");
}
associateIpRangeToAccount = true;
}
}
Transaction txn = Transaction.currentTxn();
txn.start();
Vlan vlan = createVlanAndPublicIpRange(userId, zoneId, podId, startIP, endIP, vlanGateway, vlanNetmask, forVirtualNetwork, vlanId, account, networkId);
if (associateIpRangeToAccount) {
_networkMgr.associateIpAddressListToAccount(userId, account.getId(), zoneId, vlan.getId(), network);
if (network == null) {
List<? extends Network> networks = _networkMgr.getVirtualNetworksOwnedByAccountInZone(account.getAccountName(), account.getDomainId(), zoneId);
network = networks.get(0);
}
if (network == null) {
throw new CloudRuntimeException("Failed to associate vlan to the account id=" + account.getId() + ", default network failed to create");
}
}
txn.commit();
// Associate ips to the network
if (associateIpRangeToAccount) {
if (network.getState() == Network.State.Implemented) {
s_logger.debug("Applying ip associations for vlan id=" + vlanId + " in network " + network);
if (!_networkMgr.applyIpAssociations(network, false)) {
s_logger.warn("Failed to apply ip associations for vlan id=1 as a part of add vlan range for account id=" + account.getId());
}
} else {
s_logger.trace("Network id=" + network.getId() + " is not Implemented, no need to apply ipAssociations");
}
}
return vlan;
}
@Override
@DB
public Vlan createVlanAndPublicIpRange(Long userId, Long zoneId, Long podId, String startIP, String endIP, String vlanGateway, String vlanNetmask, boolean forVirtualNetwork, String vlanId,
Account account, Long networkId) {
// Check that the pod ID is valid
if (podId != null && ((_podDao.findById(podId)) == null)) {
throw new InvalidParameterValueException("Please specify a valid pod.");
}
if (podId != null && _podDao.findById(podId).getDataCenterId() != zoneId) {
throw new InvalidParameterValueException("Pod id=" + podId + " doesn't belong to zone id=" + zoneId);
}
// If the VLAN id is null, default it to untagged
if (vlanId == null) {
vlanId = Vlan.UNTAGGED;
}
DataCenterVO zone;
if (zoneId == null || ((zone = _zoneDao.findById(zoneId)) == null)) {
throw new InvalidParameterValueException("Please specify a valid zone.");
}
// Allow adding untagged direct vlan only for Basic zone
if (zone.getNetworkType() == NetworkType.Advanced && vlanId.equals(Vlan.UNTAGGED) && (!forVirtualNetwork || zone.isSecurityGroupEnabled())) {
throw new InvalidParameterValueException("Direct untagged network is not supported for the zone " + zone.getId() + " of type " + zone.getNetworkType());
} else if (zone.getNetworkType() == NetworkType.Basic && !((vlanId.equals(Vlan.UNTAGGED) && !forVirtualNetwork) || (forVirtualNetwork))) {
throw new InvalidParameterValueException("Only Direct Untagged and Virtual networks are supported in the zone " + zone.getId() + " of type " + zone.getNetworkType());
}
// don't allow to create a virtual vlan when zone's vnet is NULL in Advanced zone
if ((zone.getNetworkType() == NetworkType.Advanced && zone.getVnet() == null) && forVirtualNetwork) {
throw new InvalidParameterValueException("Can't add virtual network to the zone id=" + zone.getId() + " as zone doesn't have guest vlan configured");
}
VlanType vlanType = forVirtualNetwork ? VlanType.VirtualNetwork : VlanType.DirectAttached;
// ACL check
checkAccess(account, zone);
if (vlanType.equals(VlanType.DirectAttached)) {
if (account != null) {
// VLANs for an account must be tagged
if (vlanId.equals(Vlan.UNTAGGED)) {
throw new InvalidParameterValueException("Direct Attached IP ranges for an account must be tagged.");
}
// Make sure there aren't any pod VLANs in this zone
List<HostPodVO> podsInZone = _podDao.listByDataCenterId(zone.getId());
for (HostPodVO pod : podsInZone) {
if (_podVlanMapDao.listPodVlanMapsByPod(pod.getId()).size() > 0) {
throw new InvalidParameterValueException("Zone " + zone.getName()
+ " already has pod-wide IP ranges. A zone may contain either pod-wide IP ranges or account-wide IP ranges, but not both.");
}
}
} else if (podId != null) {
// Pod-wide VLANs must be untagged
if (!vlanId.equals(Vlan.UNTAGGED)) {
throw new InvalidParameterValueException("Direct Attached IP ranges for a pod must be untagged.");
}
// Make sure there aren't any account VLANs in this zone
List<AccountVlanMapVO> accountVlanMaps = _accountVlanMapDao.listAllIncludingRemoved();
for (AccountVlanMapVO accountVlanMap : accountVlanMaps) {
VlanVO vlan = _vlanDao.findById(accountVlanMap.getVlanDbId());
if (vlan.getDataCenterId() == zone.getId()) {
throw new InvalidParameterValueException("Zone " + zone.getName()
+ " already has account-wide IP ranges. A zone may contain either pod-wide IP ranges or account-wide IP ranges, but not both.");
}
}
}
}
// Make sure the gateway is valid
if (!NetUtils.isValidIp(vlanGateway)) {
throw new InvalidParameterValueException("Please specify a valid gateway");
}
// Make sure the netmask is valid
if (!NetUtils.isValidIp(vlanNetmask)) {
throw new InvalidParameterValueException("Please specify a valid netmask");
}
String newVlanSubnet = NetUtils.getSubNet(vlanGateway, vlanNetmask);
// Check if the new VLAN's subnet conflicts with the guest network in the specified zone (guestCidr is null for basic
// zone)
String guestNetworkCidr = zone.getGuestNetworkCidr();
if (guestNetworkCidr != null) {
String[] cidrPair = guestNetworkCidr.split("\\/");
String guestIpNetwork = NetUtils.getIpRangeStartIpFromCidr(cidrPair[0], Long.parseLong(cidrPair[1]));
long guestCidrSize = Long.parseLong(cidrPair[1]);
long vlanCidrSize = NetUtils.getCidrSize(vlanNetmask);
long cidrSizeToUse = -1;
if (vlanCidrSize < guestCidrSize) {
cidrSizeToUse = vlanCidrSize;
} else {
cidrSizeToUse = guestCidrSize;
}
String guestSubnet = NetUtils.getCidrSubNet(guestIpNetwork, cidrSizeToUse);
if (newVlanSubnet.equals(guestSubnet)) {
throw new InvalidParameterValueException("The new IP range you have specified has the same subnet as the guest network in zone: " + zone.getName()
+ ". Please specify a different gateway/netmask.");
}
}
// Check if there are any errors with the IP range
checkPublicIpRangeErrors(zoneId, vlanId, vlanGateway, vlanNetmask, startIP, endIP);
// Throw an exception if any of the following is true:
// 1. Another VLAN in the same zone has a different tag but the same subnet as the new VLAN. Make an exception for the
// case when both vlans are Direct.
// 2. Another VLAN in the same zone that has the same tag and subnet as the new VLAN has IPs that overlap with the IPs
// being added
// 3. Another VLAN in the same zone that has the same tag and subnet as the new VLAN has a different gateway than the
// new VLAN
List<VlanVO> vlans = _vlanDao.listByZone(zone.getId());
for (VlanVO vlan : vlans) {
String otherVlanGateway = vlan.getVlanGateway();
String otherVlanSubnet = NetUtils.getSubNet(vlan.getVlanGateway(), vlan.getVlanNetmask());
String[] otherVlanIpRange = vlan.getIpRange().split("\\-");
String otherVlanStartIP = otherVlanIpRange[0];
String otherVlanEndIP = null;
if (otherVlanIpRange.length > 1) {
otherVlanEndIP = otherVlanIpRange[1];
}
if (!vlanId.equals(vlan.getVlanTag()) && newVlanSubnet.equals(otherVlanSubnet) && !allowIpRangeOverlap(vlan, forVirtualNetwork, networkId)) {
throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " in zone " + zone.getName()
+ " has the same subnet. Please specify a different gateway/netmask.");
}
if (vlanId.equals(vlan.getVlanTag()) && newVlanSubnet.equals(otherVlanSubnet)) {
if (NetUtils.ipRangesOverlap(startIP, endIP, otherVlanStartIP, otherVlanEndIP)) {
throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag()
+ " already has IPs that overlap with the new range. Please specify a different start IP/end IP.");
}
if (!vlanGateway.equals(otherVlanGateway)) {
throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " has already been added with gateway " + otherVlanGateway
+ ". Please specify a different tag.");
}
}
}
// Check if a guest VLAN is using the same tag
if (_zoneDao.findVnet(zoneId, vlanId).size() > 0) {
throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for the guest network in zone " + zone.getName());
}
// For untagged vlan check if vlan per pod already exists. If yes, verify that new vlan range has the same netmask and
// gateway
if (zone.getNetworkType() == NetworkType.Basic && vlanId.equalsIgnoreCase(Vlan.UNTAGGED) && podId != null) {
List<VlanVO> podVlans = _vlanDao.listVlansForPodByType(podId, VlanType.DirectAttached);
if (podVlans != null && !podVlans.isEmpty()) {
VlanVO podVlan = podVlans.get(0);
if (!podVlan.getVlanNetmask().equals(vlanNetmask)) {
throw new InvalidParameterValueException("Vlan netmask is different from the netmask of Untagged vlan id=" + podVlan.getId() + " existing in the pod " + podId);
} else if (!podVlan.getVlanGateway().equals(vlanGateway)) {
throw new InvalidParameterValueException("Vlan gateway is different from the gateway of Untagged vlan id=" + podVlan.getId() + " existing in the pod " + podId);
}
}
}
String ipRange = startIP;
if (endIP != null) {
ipRange += "-" + endIP;
}
// Everything was fine, so persist the VLAN
Transaction txn = Transaction.currentTxn();
txn.start();
VlanVO vlan = new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId);
vlan = _vlanDao.persist(vlan);
if (!savePublicIPRange(startIP, endIP, zoneId, vlan.getId(), networkId)) {
throw new CloudRuntimeException("Failed to save IP range. Please contact Cloud Support."); // It can be Direct IP or
// Public IP.
}
if (account != null) {
// This VLAN is account-specific, so create an AccountVlanMapVO entry
AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(account.getId(), vlan.getId());
_accountVlanMapDao.persist(accountVlanMapVO);
} else if (podId != null) {
// This VLAN is pod-wide, so create a PodVlanMapVO entry
PodVlanMapVO podVlanMapVO = new PodVlanMapVO(podId, vlan.getId());
_podVlanMapDao.persist(podVlanMapVO);
}
txn.commit();
return vlan;
}
@Override
public boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId) {
VlanVO vlan = _vlanDao.findById(vlanDbId);
if (vlan == null) {
throw new InvalidParameterValueException("Please specify a valid IP range id.");
}
// Check if the VLAN has any allocated public IPs
if (_publicIpAddressDao.countIPs(vlan.getDataCenterId(), vlanDbId, true) > 0) {
throw new InvalidParameterValueException("The IP range can't be deleted because it has allocated public IP addresses.");
}
// Delete all public IPs in the VLAN
if (!deletePublicIPRange(vlanDbId)) {
return false;
}
// Delete the VLAN
return _vlanDao.expunge(vlanDbId);
}
@Override
public List<String> csvTagsToList(String tags) {
List<String> tagsList = new ArrayList<String>();
if (tags != null) {
String[] tokens = tags.split(",");
for (int i = 0; i < tokens.length; i++) {
tagsList.add(tokens[i].trim());
}
}
return tagsList;
}
@Override
public String listToCsvTags(List<String> tagsList) {
String tags = "";
if (tagsList.size() > 0) {
for (int i = 0; i < tagsList.size(); i++) {
tags += tagsList.get(i);
if (i != tagsList.size() - 1) {
tags += ",";
}
}
}
return tags;
}
private String cleanupTags(String tags) {
if (tags != null) {
String[] tokens = tags.split(",");
StringBuilder t = new StringBuilder();
for (int i = 0; i < tokens.length; i++) {
t.append(tokens[i].trim()).append(",");
}
t.delete(t.length() - 1, t.length());
tags = t.toString();
}
return tags;
}
private boolean isPrivateIPAllocated(String ip, long podId, long zoneId, PreparedStatement stmt) {
try {
stmt.clearParameters();
stmt.setString(1, ip);
stmt.setLong(2, zoneId);
stmt.setLong(3, podId);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return (rs.getString("taken") != null);
} else {
return false;
}
} catch (SQLException ex) {
System.out.println(ex.getMessage());
return true;
}
}
@DB
protected boolean deletePublicIPRange(long vlanDbId) {
Transaction txn = Transaction.currentTxn();
String deleteSql = "DELETE FROM `cloud`.`user_ip_address` WHERE vlan_db_id = ?";
txn.start();
try {
PreparedStatement stmt = txn.prepareAutoCloseStatement(deleteSql);
stmt.setLong(1, vlanDbId);
stmt.executeUpdate();
} catch (Exception ex) {
return false;
}
txn.commit();
return true;
}
@DB
protected List<String> deletePrivateIPRange(String startIP, String endIP, long podId, long zoneId) {
long startIPLong = NetUtils.ip2Long(startIP);
long endIPLong = NetUtils.ip2Long(endIP);
Transaction txn = Transaction.currentTxn();
String deleteSql = "DELETE FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND pod_id = ? AND data_center_id = ?";
String isPrivateIPAllocatedSelectSql = "SELECT * FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND data_center_id = ? AND pod_id = ?";
List<String> problemIPs = new ArrayList<String>();
PreparedStatement deleteIPStmt = null;
PreparedStatement isAllocatedStmt = null;
txn.start();
try {
deleteIPStmt = txn.prepareAutoCloseStatement(deleteSql);
isAllocatedStmt = txn.prepareAutoCloseStatement(isPrivateIPAllocatedSelectSql);
} catch (SQLException e) {
return null;
}
while (startIPLong <= endIPLong) {
if (!isPrivateIPAllocated(NetUtils.long2Ip(startIPLong), podId, zoneId, isAllocatedStmt)) {
try {
deleteIPStmt.clearParameters();
deleteIPStmt.setString(1, NetUtils.long2Ip(startIPLong));
deleteIPStmt.setLong(2, podId);
deleteIPStmt.setLong(3, zoneId);
deleteIPStmt.executeUpdate();
} catch (Exception ex) {
}
} else {
problemIPs.add(NetUtils.long2Ip(startIPLong));
}
startIPLong += 1;
}
txn.commit();
return problemIPs;
}
@DB
protected boolean savePublicIPRange(String startIP, String endIP, long zoneId, long vlanDbId, long sourceNetworkid) {
long startIPLong = NetUtils.ip2Long(startIP);
long endIPLong = NetUtils.ip2Long(endIP);
Transaction txn = Transaction.currentTxn();
txn.start();
IPRangeConfig config = new IPRangeConfig();
config.savePublicIPRange(txn, startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkid);
txn.commit();
return true;
}
@DB
protected List<String> savePrivateIPRange(String startIP, String endIP, long podId, long zoneId) {
Transaction txn = Transaction.currentTxn();
IPRangeConfig config = new IPRangeConfig();
txn.start();
List<String> ips = config.savePrivateIPRange(txn, NetUtils.ip2Long(startIP), NetUtils.ip2Long(endIP), podId, zoneId);
txn.commit();
return ips;
}
private String genChangeRangeSuccessString(List<String> problemIPs, boolean add) {
if (problemIPs == null) {
return "";
}
if (problemIPs.size() == 0) {
if (add) {
return "Successfully added all IPs in the specified range.";
} else {
return "Successfully deleted all IPs in the specified range.";
}
} else {
String successString = "";
if (add) {
successString += "Failed to add the following IPs, because they are already in the database: ";
} else {
successString += "Failed to delete the following IPs, because they are in use: ";
}
for (int i = 0; i < problemIPs.size(); i++) {
successString += problemIPs.get(i);
if (i != (problemIPs.size() - 1)) {
successString += ", ";
}
}
successString += ". ";
if (add) {
successString += "Successfully added all other IPs in the specified range.";
} else {
successString += "Successfully deleted all other IPs in the specified range.";
}
return successString;
}
}
private void checkPublicIpRangeErrors(long zoneId, String vlanId, String vlanGateway, String vlanNetmask, String startIP, String endIP) {
// Check that the start and end IPs are valid
if (!NetUtils.isValidIp(startIP)) {
throw new InvalidParameterValueException("Please specify a valid start IP");
}
if (endIP != null && !NetUtils.isValidIp(endIP)) {
throw new InvalidParameterValueException("Please specify a valid end IP");
}
if (endIP != null && !NetUtils.validIpRange(startIP, endIP)) {
throw new InvalidParameterValueException("Please specify a valid IP range.");
}
// Check that the IPs that are being added are compatible with the VLAN's gateway and netmask
if (vlanNetmask == null) {
throw new InvalidParameterValueException("Please ensure that your IP range's netmask is specified");
}
if (endIP != null && !NetUtils.sameSubnet(startIP, endIP, vlanNetmask)) {
throw new InvalidParameterValueException("Please ensure that your start IP and end IP are in the same subnet, as per the IP range's netmask.");
}
if (!NetUtils.sameSubnet(startIP, vlanGateway, vlanNetmask)) {
throw new InvalidParameterValueException("Please ensure that your start IP is in the same subnet as your IP range's gateway, as per the IP range's netmask.");
}
if (endIP != null && !NetUtils.sameSubnet(endIP, vlanGateway, vlanNetmask)) {
throw new InvalidParameterValueException("Please ensure that your end IP is in the same subnet as your IP range's gateway, as per the IP range's netmask.");
}
}
private void checkPrivateIpRangeErrors(Long podId, String startIP, String endIP) {
HostPodVO pod = _podDao.findById(podId);
if (pod == null) {
throw new InvalidParameterValueException("Please specify a valid pod.");
}
// Check that the start and end IPs are valid
if (!NetUtils.isValidIp(startIP)) {
throw new InvalidParameterValueException("Please specify a valid start IP");
}
if (endIP != null && !NetUtils.isValidIp(endIP)) {
throw new InvalidParameterValueException("Please specify a valid end IP");
}
if (endIP != null && !NetUtils.validIpRange(startIP, endIP)) {
throw new InvalidParameterValueException("Please specify a valid IP range.");
}
// Check that the IPs that are being added are compatible with the pod's CIDR
String cidrAddress = getCidrAddress(podId);
long cidrSize = getCidrSize(podId);
if (endIP != null && !NetUtils.sameSubnetCIDR(startIP, endIP, cidrSize)) {
throw new InvalidParameterValueException("Please ensure that your start IP and end IP are in the same subnet, as per the pod's CIDR size.");
}
if (!NetUtils.sameSubnetCIDR(startIP, cidrAddress, cidrSize)) {
throw new InvalidParameterValueException("Please ensure that your start IP is in the same subnet as the pod's CIDR address.");
}
if (endIP != null && !NetUtils.sameSubnetCIDR(endIP, cidrAddress, cidrSize)) {
throw new InvalidParameterValueException("Please ensure that your end IP is in the same subnet as the pod's CIDR address.");
}
}
private String getCidrAddress(String cidr) {
String[] cidrPair = cidr.split("\\/");
return cidrPair[0];
}
private int getCidrSize(String cidr) {
String[] cidrPair = cidr.split("\\/");
return Integer.parseInt(cidrPair[1]);
}
private String getCidrAddress(long podId) {
HostPodVO pod = _podDao.findById(podId);
return pod.getCidrAddress();
}
private long getCidrSize(long podId) {
HostPodVO pod = _podDao.findById(podId);
return pod.getCidrSize();
}
private void checkPodCidrSubnets(long dcId, HashMap<Long, List<Object>> currentPodCidrSubnets) {
// For each pod, return an error if any of the following is true:
// 1. The pod's CIDR subnet conflicts with the guest network subnet
// 2. The pod's CIDR subnet conflicts with the CIDR subnet of any other pod
DataCenterVO dcVo = _zoneDao.findById(dcId);
String guestNetworkCidr = dcVo.getGuestNetworkCidr();
// Guest cidr can be null for Basic zone
String guestIpNetwork = null;
Long guestCidrSize = null;
if (guestNetworkCidr != null) {
String[] cidrTuple = guestNetworkCidr.split("\\/");
guestIpNetwork = NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1]));
guestCidrSize = Long.parseLong(cidrTuple[1]);
}
String zoneName = getZoneName(dcId);
// Iterate through all pods in this zone
for (Long podId : currentPodCidrSubnets.keySet()) {
String podName;
if (podId.longValue() == -1) {
podName = "newPod";
} else {
podName = getPodName(podId.longValue());
}
List<Object> cidrPair = currentPodCidrSubnets.get(podId);
String cidrAddress = (String) cidrPair.get(0);
long cidrSize = ((Long) cidrPair.get(1)).longValue();
long cidrSizeToUse = -1;
if (guestCidrSize == null || cidrSize < guestCidrSize) {
cidrSizeToUse = cidrSize;
} else {
cidrSizeToUse = guestCidrSize;
}
String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSizeToUse);
if (guestNetworkCidr != null) {
String guestSubnet = NetUtils.getCidrSubNet(guestIpNetwork, cidrSizeToUse);
// Check that cidrSubnet does not equal guestSubnet
if (cidrSubnet.equals(guestSubnet)) {
if (podName.equals("newPod")) {
throw new InvalidParameterValueException("The subnet of the pod you are adding conflicts with the subnet of the Guest IP Network. Please specify a different CIDR.");
} else {
throw new InvalidParameterValueException("Warning: The subnet of pod " + podName + " in zone " + zoneName
+ " conflicts with the subnet of the Guest IP Network. Please change either the pod's CIDR or the Guest IP Network's subnet, and re-run install-vmops-management.");
}
}
}
// Iterate through the rest of the pods
for (Long otherPodId : currentPodCidrSubnets.keySet()) {
if (podId.equals(otherPodId)) {
continue;
}
// Check that cidrSubnet does not equal otherCidrSubnet
List<Object> otherCidrPair = currentPodCidrSubnets.get(otherPodId);
String otherCidrAddress = (String) otherCidrPair.get(0);
long otherCidrSize = ((Long) otherCidrPair.get(1)).longValue();
if (cidrSize < otherCidrSize) {
cidrSizeToUse = cidrSize;
} else {
cidrSizeToUse = otherCidrSize;
}
cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSizeToUse);
String otherCidrSubnet = NetUtils.getCidrSubNet(otherCidrAddress, cidrSizeToUse);
if (cidrSubnet.equals(otherCidrSubnet)) {
String otherPodName = getPodName(otherPodId.longValue());
if (podName.equals("newPod")) {
throw new InvalidParameterValueException("The subnet of the pod you are adding conflicts with the subnet of pod " + otherPodName + " in zone " + zoneName
+ ". Please specify a different CIDR.");
} else {
throw new InvalidParameterValueException("Warning: The pods " + podName + " and " + otherPodName + " in zone " + zoneName
+ " have conflicting CIDR subnets. Please change the CIDR of one of these pods.");
}
}
}
}
}
private boolean validPod(long podId) {
return (_podDao.findById(podId) != null);
}
private boolean validPod(String podName, long zoneId) {
if (!validZone(zoneId)) {
return false;
}
return (_podDao.findByName(podName, zoneId) != null);
}
private String getPodName(long podId) {
return _podDao.findById(new Long(podId)).getName();
}
private boolean validZone(String zoneName) {
return (_zoneDao.findByName(zoneName) != null);
}
private boolean validZone(long zoneId) {
return (_zoneDao.findById(zoneId) != null);
}
private String getZoneName(long zoneId) {
DataCenterVO zone = _zoneDao.findById(new Long(zoneId));
if (zone != null) {
return zone.getName();
} else {
return null;
}
}
private String[] getLinkLocalIPRange() {
String ipNums = _configDao.getValue("linkLocalIp.nums");
int nums = Integer.parseInt(ipNums);
if (nums > 16 || nums <= 0) {
throw new InvalidParameterValueException("The linkLocalIp.nums: " + nums + "is wrong, should be 1~16");
}
/* local link ip address starts from 169.254.0.2 - 169.254.(nums) */
String[] ipRanges = NetUtils.getLinkLocalIPRange(nums);
if (ipRanges == null) {
throw new InvalidParameterValueException("The linkLocalIp.nums: " + nums + "may be wrong, should be 1~16");
}
return ipRanges;
}
@Override
public Configuration addConfig(CreateCfgCmd cmd) {
String category = cmd.getCategory();
String instance = cmd.getInstance();
String component = cmd.getComponent();
String name = cmd.getConfigPropName();
String value = cmd.getValue();
String description = cmd.getDescription();
try {
ConfigurationVO entity = new ConfigurationVO(category, instance, component, name, value, description);
_configDao.persist(entity);
s_logger.info("Successfully added configuration value into db: category:" + category + " instance:" + instance + " component:" + component + " name:" + name + " value:" + value);
return _configDao.findByName(name);
} catch (Exception ex) {
s_logger.error("Unable to add the new config entry:", ex);
throw new CloudRuntimeException("Unable to add configuration parameter " + name);
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DELETE, eventDescription = "deleting vlan ip range", async = false)
public boolean deleteVlanIpRange(DeleteVlanIpRangeCmd cmd) {
Long vlanDbId = cmd.getId();
Long userId = UserContext.current().getCallerUserId();
if (userId == null) {
userId = Long.valueOf(User.UID_SYSTEM);
}
VlanVO vlan = _vlanDao.findById(vlanDbId);
if (vlan == null) {
throw new InvalidParameterValueException("Please specify a valid IP range id.");
}
return deleteVlanAndPublicIpRange(userId, vlanDbId);
}
@Override
public void checkDiskOfferingAccess(Account caller, DiskOffering dof) throws PermissionDeniedException {
for (SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, dof)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to disk offering:" + dof.getId() + " by " + checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName());
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to disk offering:" + dof.getId());
}
@Override
public void checkServiceOfferingAccess(Account caller, ServiceOffering so) throws PermissionDeniedException {
for (SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, so)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to service offering:" + so.getId() + " by " + checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName());
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to service offering:" + so.getId());
}
@Override
public void checkAccess(Account caller, DataCenter zone) throws PermissionDeniedException {
for (SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, zone)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to zone:" + zone.getId() + " by " + checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName());
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to zone:" + zone.getId());
}
@Override
public NetworkOffering createNetworkOffering(CreateNetworkOfferingCmd cmd) {
Long userId = UserContext.current().getCallerUserId();
String name = cmd.getNetworkOfferingName();
String displayText = cmd.getDisplayText();
String tags = cmd.getTags();
String trafficTypeString = cmd.getTraffictype();
Boolean specifyVlan = cmd.getSpecifyVlan();
String availabilityStr = cmd.getAvailability();
String guestIpTypeString = cmd.getGuestIpType();
TrafficType trafficType = null;
GuestIpType guestIpType = null;
Availability availability = null;
// Verify traffic type
for (TrafficType tType : TrafficType.values()) {
if (tType.name().equalsIgnoreCase(trafficTypeString)) {
trafficType = tType;
break;
}
}
if (trafficType == null) {
throw new InvalidParameterValueException("Invalid value for traffictype. Supported traffic types: Public, Management, Control, Guest, Vlan or Storage");
}
// Verify guest ip type
for (GuestIpType gType : GuestIpType.values()) {
if (gType.name().equalsIgnoreCase(guestIpTypeString)) {
guestIpType = gType;
break;
}
}
if (guestIpType == null) {
throw new InvalidParameterValueException("Invalid guest IP type; can have Direct or Virtual value");
}
// Verify availability
for (Availability avlb : Availability.values()) {
if (avlb.name().equalsIgnoreCase(availabilityStr)) {
availability = avlb;
}
}
if (availability == null) {
throw new InvalidParameterValueException("Invalid value for Availability. Supported types: " + Availability.Required + ", " + Availability.Optional + ", " + Availability.Unavailable);
}
Integer maxConnections = cmd.getMaxconnections();
return createNetworkOffering(userId, name, displayText, trafficType, tags, maxConnections, specifyVlan, availability, guestIpType);
}
@Override
public NetworkOfferingVO createNetworkOffering(long userId, String name, String displayText, TrafficType trafficType, String tags, Integer maxConnections, boolean specifyVlan,
Availability availability, GuestIpType guestIpType) {
String networkRateStr = _configDao.getValue("network.throttling.rate");
String multicastRateStr = _configDao.getValue("multicast.throttling.rate");
int networkRate = ((networkRateStr == null) ? 200 : Integer.parseInt(networkRateStr));
int multicastRate = ((multicastRateStr == null) ? 10 : Integer.parseInt(multicastRateStr));
tags = cleanupTags(tags);
boolean firewallService = false;
boolean lbService = false;
boolean vpnService = false;
boolean gatewayService = false;
if (trafficType == TrafficType.Guest) {
firewallService = true;
lbService = true;
vpnService = true;
gatewayService = true;
}
NetworkOfferingVO offering = new NetworkOfferingVO(name, displayText, trafficType, false, specifyVlan, networkRate, multicastRate, maxConnections, false, availability, true, true, true,
gatewayService, firewallService, lbService, vpnService, guestIpType, false);
if ((offering = _networkOfferingDao.persist(offering)) != null) {
return offering;
} else {
return null;
}
}
@Override
public List<? extends NetworkOffering> searchForNetworkOfferings(ListNetworkOfferingsCmd cmd) {
Filter searchFilter = new Filter(NetworkOfferingVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal());
SearchCriteria<NetworkOfferingVO> sc = _networkOfferingDao.createSearchCriteria();
Object id = cmd.getId();
Object name = cmd.getNetworkOfferingName();
Object displayText = cmd.getDisplayText();
Object trafficType = cmd.getTrafficType();
Object isDefault = cmd.getIsDefault();
Object specifyVlan = cmd.getSpecifyVlan();
Object isShared = cmd.getIsShared();
Object availability = cmd.getAvailability();
Object guestIpType = cmd.getGuestIpType();
Long zoneId = cmd.getZoneId();
DataCenter zone = null;
if (zoneId != null) {
zone = getZone(zoneId);
}
Object keyword = cmd.getKeyword();
if (keyword != null) {
SearchCriteria<NetworkOfferingVO> ssc = _networkOfferingDao.createSearchCriteria();
ssc.addOr("displayText", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
sc.addAnd("name", SearchCriteria.Op.SC, ssc);
}
if (id != null) {
sc.addAnd("id", SearchCriteria.Op.EQ, id);
}
if (guestIpType != null) {
sc.addAnd("guestType", SearchCriteria.Op.EQ, guestIpType);
}
if (name != null) {
sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + name + "%");
}
if (displayText != null) {
sc.addAnd("displayText", SearchCriteria.Op.LIKE, "%" + displayText + "%");
}
if (trafficType != null) {
sc.addAnd("trafficType", SearchCriteria.Op.EQ, trafficType);
}
if (isDefault != null) {
sc.addAnd("isDefault", SearchCriteria.Op.EQ, isDefault);
}
if (specifyVlan != null) {
sc.addAnd("specifyVlan", SearchCriteria.Op.EQ, specifyVlan);
}
if (isShared != null) {
sc.addAnd("isShared", SearchCriteria.Op.EQ, isShared);
}
if (availability != null) {
sc.addAnd("availability", SearchCriteria.Op.EQ, availability);
}
if (zone != null) {
if (zone.getNetworkType() == NetworkType.Basic) {
// return empty list as we don't allow to create networks in basic zone, and shouldn't display networkOfferings
return new ArrayList<NetworkOffering>();
} else if (zone.isSecurityGroupEnabled()) {
sc.addAnd("guestType", SearchCriteria.Op.EQ, GuestIpType.Direct);
}
}
// Don't return system network offerings to the user
sc.addAnd("systemOnly", SearchCriteria.Op.EQ, false);
return _networkOfferingDao.search(sc, searchFilter);
}
@Override
public boolean deleteNetworkOffering(DeleteNetworkOfferingCmd cmd) {
Long offeringId = cmd.getId();
// Verify network offering id
NetworkOfferingVO offering = _networkOfferingDao.findById(offeringId);
if (offering == null) {
throw new InvalidParameterValueException("unable to find network offering " + offeringId);
} else if (offering.getRemoved() != null || offering.isSystemOnly()) {
throw new InvalidParameterValueException("unable to find network offering " + offeringId);
}
// Don't allow to delete default network offerings
if (offering.isDefault() == true) {
throw new InvalidParameterValueException("Default network offering can't be deleted");
}
if (_networkOfferingDao.remove(offeringId)) {
return true;
} else {
return false;
}
}
@Override
public NetworkOffering updateNetworkOffering(UpdateNetworkOfferingCmd cmd) {
String displayText = cmd.getDisplayText();
Long id = cmd.getId();
String availabilityStr = cmd.getAvailability();
Availability availability = null;
// Verify input parameters
NetworkOfferingVO offeringToUpdate = _networkOfferingDao.findById(id);
if (offeringToUpdate == null) {
throw new InvalidParameterValueException("unable to find network offering " + id);
}
// Don't allow to update system network offering
if (offeringToUpdate.isSystemOnly()) {
throw new InvalidParameterValueException("Can't update system network offerings");
}
// Don't allow to update default Direct network offering
if (offeringToUpdate.isDefault() && offeringToUpdate.getGuestType() == GuestIpType.Direct) {
throw new InvalidParameterValueException("Can't update Default Direct network offering");
}
NetworkOfferingVO offering = _networkOfferingDao.createForUpdate(id);
if (displayText != null) {
offering.setDisplayText(displayText);
}
// Verify availability
if (availabilityStr != null) {
for (Availability avlb : Availability.values()) {
if (avlb.name().equalsIgnoreCase(availabilityStr)) {
availability = avlb;
}
}
if (availability == null) {
throw new InvalidParameterValueException("Invalid value for Availability. Supported types: " + Availability.Required + ", " + Availability.Optional + ", " + Availability.Unavailable);
} else {
offering.setAvailability(availability);
}
}
if (_networkOfferingDao.update(id, offering)) {
offering = _networkOfferingDao.findById(id);
return offering;
} else {
return null;
}
}
// Note: This method will be used for entity name validations in the coming releases (place holder for now)
private void validateEntityName(String str) {
String forbidden = "~!@#$%^&*()+=";
char[] searchChars = forbidden.toCharArray();
if (str == null || str.length() == 0 || searchChars == null || searchChars.length == 0) {
return;
}
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
for (int j = 0; j < searchChars.length; j++) {
if (searchChars[j] == ch) {
throw new InvalidParameterValueException("Name cannot contain any of the following special characters:" + forbidden);
}
}
}
}
@Override
public DataCenterVO getZone(long id) {
return _zoneDao.findById(id);
}
@Override
public NetworkOffering getNetworkOffering(long id) {
return _networkOfferingDao.findById(id);
}
@Override
public Integer getNetworkOfferingNetworkRate(long networkOfferingId) {
// validate network offering information
NetworkOffering no = getNetworkOffering(networkOfferingId);
if (no == null) {
throw new InvalidParameterValueException("Unable to find network offering by id=" + networkOfferingId);
}
Integer networkRate;
if (no.getRateMbps() != null) {
networkRate = no.getRateMbps();
} else {
networkRate = Integer.parseInt(_configDao.getValue(Config.NetworkThrottlingRate.key()));
}
// networkRate is unsigned int in netowrkOfferings table, and can't be set to -1
// so 0 means unlimited; we convert it to -1, so we are consistent with all our other resources where -1 means unlimited
if (networkRate == 0) {
networkRate = -1;
}
return networkRate;
}
@Override
public Account getVlanAccount(long vlanId) {
Vlan vlan = _vlanDao.findById(vlanId);
Long accountId = null;
// if vlan is Virtual Account specific, get vlan information from the accountVlanMap; otherwise get account information
// from the network
if (vlan.getVlanType() == VlanType.VirtualNetwork) {
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanId);
if (maps != null && !maps.isEmpty()) {
return _accountMgr.getAccount(maps.get(0).getAccountId());
}
}
Long networkId = vlan.getNetworkId();
if (networkId != null) {
Network network = _networkMgr.getNetwork(networkId);
if (network != null) {
accountId = network.getAccountId();
}
}
return _accountMgr.getAccount(accountId);
}
@Override
public List<? extends NetworkOffering> listNetworkOfferings(TrafficType trafficType, boolean systemOnly) {
Filter searchFilter = new Filter(NetworkOfferingVO.class, "created", false, null, null);
SearchCriteria<NetworkOfferingVO> sc = _networkOfferingDao.createSearchCriteria();
if (trafficType != null) {
sc.addAnd("trafficType", SearchCriteria.Op.EQ, trafficType);
}
sc.addAnd("systemOnly", SearchCriteria.Op.EQ, systemOnly);
return _networkOfferingDao.search(sc, searchFilter);
}
@Override
@DB
public boolean deleteAccountSpecificVirtualRanges(long accountId) {
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByAccount(accountId);
boolean result = true;
if (maps != null && !maps.isEmpty()) {
Transaction txn = Transaction.currentTxn();
txn.start();
for (AccountVlanMapVO map : maps) {
if (!deleteVlanAndPublicIpRange(_accountMgr.getSystemUser().getId(), map.getVlanDbId())) {
result = false;
}
}
if (result) {
txn.commit();
} else {
s_logger.error("Failed to delete account specific virtual ip ranges for account id=" + accountId);
}
} else {
s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to delete");
}
return result;
}
@Override
public HostPodVO getPod(long id) {
return _podDao.findById(id);
}
@Override
public ClusterVO getCluster(long id) {
return _clusterDao.findById(id);
}
private boolean allowIpRangeOverlap(VlanVO vlan, boolean forVirtualNetwork, long networkId) {
Network vlanNetwork = _networkMgr.getNetwork(vlan.getNetworkId());
Network network = _networkMgr.getNetwork(networkId);
if (vlan.getVlanType() == VlanType.DirectAttached && !forVirtualNetwork && network.getAccountId() != vlanNetwork.getAccountId()) {
return true;
} else {
return false;
}
}
@Override
public ServiceOffering getServiceOffering(long serviceOfferingId) {
return _serviceOfferingDao.findById(serviceOfferingId);
}
@Override
public Long getDefaultPageSize() {
return _defaultPageSize;
}
@Override
public Integer getServiceOfferingNetworkRate(long serviceOfferingId) {
// validate network offering information
ServiceOffering offering = getServiceOffering(serviceOfferingId);
if (offering == null) {
throw new InvalidParameterValueException("Unable to find service offering by id=" + serviceOfferingId);
}
Integer networkRate;
if (offering.getRateMbps() != null) {
networkRate = offering.getRateMbps();
} else {
networkRate = Integer.parseInt(_configDao.getValue(Config.VmNetworkThrottlingRate.key()));
}
// networkRate is unsigned int in serviceOffering table, and can't be set to -1
// so 0 means unlimited; we convert it to -1, so we are consistent with all our other resources where -1 means unlimited
if (networkRate == 0) {
networkRate = -1;
}
return networkRate;
}
}