blob: ac43c11fbe2f2f7054fac725af64c5f6193244a5 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.network;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.api.command.admin.network.CreateStorageNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.DeleteStorageNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.ListStorageNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.UpdateStorageNetworkIpRangeCmd;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.StorageNetworkIpAddressVO;
import com.cloud.dc.StorageNetworkIpRange;
import com.cloud.dc.StorageNetworkIpRangeVO;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.dc.dao.StorageNetworkIpAddressDao;
import com.cloud.dc.dao.StorageNetworkIpRangeDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.QueryBuilder;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionCallbackWithException;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.SecondaryStorageVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.SecondaryStorageVmDao;
@Component
public class StorageNetworkManagerImpl extends ManagerBase implements StorageNetworkManager, StorageNetworkService {
private static final Logger s_logger = Logger.getLogger(StorageNetworkManagerImpl.class);
@Inject
StorageNetworkIpAddressDao _sNwIpDao;
@Inject
StorageNetworkIpRangeDao _sNwIpRangeDao;
@Inject
NetworkDao _networkDao;
@Inject
HostPodDao _podDao;
@Inject
SecondaryStorageVmDao _ssvmDao;
private void checkOverlapPrivateIpRange(long podId, String startIp, String endIp) {
HostPodVO pod = _podDao.findById(podId);
if (pod == null) {
throw new CloudRuntimeException("Cannot find pod " + podId);
}
final String[] existingPodIpRanges = pod.getDescription().split(",");
for(String podIpRange: existingPodIpRanges) {
final String[] existingPodIpRange = podIpRange.split("-");
if (existingPodIpRange.length > 1) {
if (!NetUtils.isValidIp4(existingPodIpRange[0]) || !NetUtils.isValidIp4(existingPodIpRange[1])) {
continue;
}
if (NetUtils.ipRangesOverlap(startIp, endIp, existingPodIpRange[0], existingPodIpRange[1])) {
throw new InvalidParameterValueException("The Storage network Start IP and endIP address range overlap with private IP :" + existingPodIpRange[0] + ":" + existingPodIpRange[1]);
}
}
}
}
private void checkOverlapStorageIpRange(long podId, String startIp, String endIp) {
List<StorageNetworkIpRangeVO> curRanges = _sNwIpRangeDao.listByPodId(podId);
for (StorageNetworkIpRangeVO range : curRanges) {
if (NetUtils.ipRangesOverlap(startIp, endIp, range.getStartIp(), range.getEndIp())) {
throw new InvalidParameterValueException("The Storage network Start IP and endIP address range overlap with private IP :" + range.getStartIp() + " - " +
range.getEndIp());
}
}
}
private void createStorageIpEntires(TransactionLegacy txn, long rangeId, String startIp, String endIp, long zoneId) throws SQLException {
long startIPLong = NetUtils.ip2Long(startIp);
long endIPLong = NetUtils.ip2Long(endIp);
String insertSql =
"INSERT INTO `cloud`.`op_dc_storage_network_ip_address` (range_id, ip_address, mac_address, taken) VALUES (?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?)";
String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?";
Connection conn = txn.getConnection();
while (startIPLong <= endIPLong) {
try (PreparedStatement stmt_insert = conn.prepareStatement(insertSql);) {
stmt_insert.setLong(1, rangeId);
stmt_insert.setString(2, NetUtils.long2Ip(startIPLong++));
stmt_insert.setLong(3, zoneId);
stmt_insert.setNull(4, java.sql.Types.DATE);
stmt_insert.executeUpdate();
}
try (PreparedStatement stmt_update = txn.prepareStatement(updateSql);) {
stmt_update.setLong(1, zoneId);
stmt_update.executeUpdate();
}
}
}
@Override
@DB
public StorageNetworkIpRange updateIpRange(UpdateStorageNetworkIpRangeCmd cmd) {
final Integer vlan = cmd.getVlan();
final Long rangeId = cmd.getId();
String startIp = cmd.getStartIp();
String endIp = cmd.getEndIp();
final String netmask = cmd.getNetmask();
if (netmask != null && !NetUtils.isValidIp4Netmask(netmask)) {
throw new CloudRuntimeException("Invalid netmask:" + netmask);
}
if (_sNwIpDao.countInUseIpByRangeId(rangeId) > 0) {
throw new CloudRuntimeException("Cannot update the range," + getInUseIpAddress(rangeId));
}
StorageNetworkIpRangeVO range = _sNwIpRangeDao.findById(rangeId);
if (range == null) {
throw new CloudRuntimeException("Cannot find storage ip range " + rangeId);
}
if (startIp != null || endIp != null) {
long podId = range.getPodId();
startIp = startIp == null ? range.getStartIp() : startIp;
endIp = endIp == null ? range.getEndIp() : endIp;
checkOverlapPrivateIpRange(podId, startIp, endIp);
checkOverlapStorageIpRange(podId, startIp, endIp);
}
final String startIpFinal = startIp;
final String endIpFinal = endIp;
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
StorageNetworkIpRangeVO range = null;
try {
range = _sNwIpRangeDao.acquireInLockTable(rangeId);
if (range == null) {
throw new CloudRuntimeException("Cannot acquire lock on storage ip range " + rangeId);
}
StorageNetworkIpRangeVO vo = _sNwIpRangeDao.createForUpdate();
if (vlan != null) {
vo.setVlan(vlan);
}
if (startIpFinal != null) {
vo.setStartIp(startIpFinal);
}
if (endIpFinal != null) {
vo.setEndIp(endIpFinal);
}
if (netmask != null) {
vo.setNetmask(netmask);
}
_sNwIpRangeDao.update(rangeId, vo);
} finally {
if (range != null) {
_sNwIpRangeDao.releaseFromLockTable(range.getId());
}
}
}
});
return _sNwIpRangeDao.findById(rangeId);
}
@Override
@DB
public StorageNetworkIpRange createIpRange(final CreateStorageNetworkIpRangeCmd cmd) throws SQLException {
final Long podId = cmd.getPodId();
final String startIp = cmd.getStartIp();
String endIp = cmd.getEndIp();
final Integer vlan = cmd.getVlan();
final String netmask = cmd.getNetmask();
if (endIp == null) {
endIp = startIp;
}
if (!NetUtils.isValidIp4Netmask(netmask)) {
throw new CloudRuntimeException("Invalid netmask:" + netmask);
}
HostPodVO pod = _podDao.findById(podId);
if (pod == null) {
throw new CloudRuntimeException("Cannot find pod " + podId);
}
final Long zoneId = pod.getDataCenterId();
List<NetworkVO> nws = _networkDao.listByZoneAndTrafficType(zoneId, TrafficType.Storage);
if (nws.size() == 0) {
throw new CloudRuntimeException("Cannot find storage network in zone " + zoneId);
}
if (nws.size() > 1) {
throw new CloudRuntimeException("Find more than one storage network in zone " + zoneId + "," + nws.size() + " found");
}
final NetworkVO nw = nws.get(0);
checkOverlapPrivateIpRange(podId, startIp, endIp);
checkOverlapStorageIpRange(podId, startIp, endIp);
StorageNetworkIpRangeVO range = null;
final String endIpFinal = endIp;
return Transaction.execute(new TransactionCallbackWithException<StorageNetworkIpRangeVO, SQLException>() {
@Override
public StorageNetworkIpRangeVO doInTransaction(TransactionStatus status) throws SQLException {
StorageNetworkIpRangeVO range = new StorageNetworkIpRangeVO(zoneId, podId, nw.getId(), startIp, endIpFinal, vlan, netmask, cmd.getGateWay());
_sNwIpRangeDao.persist(range);
try {
createStorageIpEntires(TransactionLegacy.currentTxn(), range.getId(), startIp, endIpFinal, zoneId);
} catch (SQLException e) {
StringBuilder err = new StringBuilder();
err.append("Create storage network range failed.");
err.append("startIp=" + startIp);
err.append("endIp=" + endIpFinal);
err.append("netmask=" + netmask);
err.append("zoneId=" + zoneId);
s_logger.debug(err.toString(), e);
throw e;
}
return range;
}
});
}
private String getInUseIpAddress(long rangeId) {
List<String> ips = _sNwIpDao.listInUseIpByRangeId(rangeId);
StringBuilder res = new StringBuilder();
res.append("Below IP of range " + rangeId + " is still in use:");
for (String ip : ips) {
res.append(ip).append(",");
}
return res.toString();
}
@Override
@DB
public void deleteIpRange(DeleteStorageNetworkIpRangeCmd cmd) {
final long rangeId = cmd.getId();
StorageNetworkIpRangeVO range = _sNwIpRangeDao.findById(rangeId);
if (range == null) {
throw new CloudRuntimeException("Can not find storage network ip range " + rangeId);
}
if (_sNwIpDao.countInUseIpByRangeId(rangeId) > 0) {
throw new CloudRuntimeException(getInUseIpAddress(rangeId));
}
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
StorageNetworkIpRangeVO range = null;
try {
range = _sNwIpRangeDao.acquireInLockTable(rangeId);
if (range == null) {
String msg = "Unable to acquire lock on storage network ip range id=" + rangeId + ", delete failed";
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
/*
* entries in op_dc_storage_network_ip_address will be deleted automatically due to
* fk_storage_ip_address__range_id constraint key
*/
_sNwIpRangeDao.remove(rangeId);
} finally {
if (range != null) {
_sNwIpRangeDao.releaseFromLockTable(rangeId);
}
}
}
});
}
@Override
public List<StorageNetworkIpRange> listIpRange(ListStorageNetworkIpRangeCmd cmd) {
Long rangeId = cmd.getRangeId();
Long podId = cmd.getPodId();
Long zoneId = cmd.getZoneId();
List result = null;
if (rangeId != null) {
result = _sNwIpRangeDao.listByRangeId(rangeId);
} else if (podId != null) {
result = _sNwIpRangeDao.listByPodId(podId);
} else if (zoneId != null) {
result = _sNwIpRangeDao.listByDataCenterId(zoneId);
} else {
result = _sNwIpRangeDao.listAll();
}
return result;
}
@Override
public void releaseIpAddress(String ip) {
_sNwIpDao.releaseIpAddress(ip);
}
@Override
public StorageNetworkIpAddressVO acquireIpAddress(long podId) {
List<StorageNetworkIpRangeVO> ranges = _sNwIpRangeDao.listByPodId(podId);
for (StorageNetworkIpRangeVO r : ranges) {
try {
Long rangeId = r.getId();
r = _sNwIpRangeDao.acquireInLockTable(rangeId);
if (r == null) {
String msg = "Unable to acquire lock on storage network ip range id=" + rangeId + ", delete failed";
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
StorageNetworkIpAddressVO ip = _sNwIpDao.takeIpAddress(r.getId());
if (ip != null) {
return ip;
}
} finally {
if (r != null) {
_sNwIpRangeDao.releaseFromLockTable(r.getId());
}
}
}
return null;
}
@Override
public boolean isStorageIpRangeAvailable(long zoneId) {
QueryBuilder<StorageNetworkIpRangeVO> sc = QueryBuilder.create(StorageNetworkIpRangeVO.class);
sc.and(sc.entity().getDataCenterId(), Op.EQ, zoneId);
List<StorageNetworkIpRangeVO> entries = sc.list();
return entries.size() > 0;
}
@Override
public List<SecondaryStorageVmVO> getSSVMWithNoStorageNetwork(long zoneId) {
List<SecondaryStorageVmVO> ssvms =
_ssvmDao.getSecStorageVmListInStates(null, zoneId, VirtualMachine.State.Starting, VirtualMachine.State.Running, VirtualMachine.State.Stopping);
return ssvms;
}
@Override
public boolean isAnyStorageIpInUseInZone(long zoneId) {
List<StorageNetworkIpRangeVO> ranges = _sNwIpRangeDao.listByDataCenterId(zoneId);
for (StorageNetworkIpRangeVO r : ranges) {
if (_sNwIpDao.countInUseIpByRangeId(r.getId()) > 0) {
return true;
}
}
return false;
}
}