package org.ovirt.engine.ui.uicommonweb.models.hosts.network;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.ovirt.engine.core.common.businessentities.network.BondMode;
/**
* A Factory responsible for providing Setup Network Operations for Network Items.<BR>
* The Factory also generates Menu Items for these Operations.
*
*/
@SuppressWarnings("ChainOfInstanceofChecks")
public class NetworkOperationFactory {
public static NetworkOperation operationFor(NetworkItemModel<?> op1, NetworkItemModel<?> op2) {
return operationFor(op1, op2, false);
}
/**
* Gets the valid Operation involving the two operands.<BR>
* If no Operation is valid returns null operation
*/
public static NetworkOperation operationFor(NetworkItemModel<?> op1, NetworkItemModel<?> op2, boolean isDrag) {
if (noValidOperationForFirstOperand(op1)) {
return NetworkOperation.NULL_OPERATION;
}
boolean unaryOperation = op2 == null;
if (unaryOperation) {
return handleUnaryOperation(op1, isDrag);
} else {
return handleBinaryOperation(op1, op2);
}
}
private static NetworkOperation handleUnaryOperation(NetworkItemModel<?> op1, boolean isDrag) {
// unary operation dragging op1 to nowhere
// op1 is a bond, break it
if (op1 instanceof BondNetworkInterfaceModel) {
return NetworkOperation.BREAK_BOND;
}
// op1 is an interface, if it's bonded remove from bond
if (op1 instanceof NetworkInterfaceModel) {
NetworkInterfaceModel nic = (NetworkInterfaceModel) op1;
if (nic.isBonded()) {
return NetworkOperation.REMOVE_FROM_BOND;
} else {
return NetworkOperation.NULL_OPERATION;
}
}
// op1 is a network, detach it if already attached to a NIC
if (op1 instanceof LogicalNetworkModel) {
LogicalNetworkModel network = (LogicalNetworkModel) op1;
if (network.isAttached()) {
if (!network.isManaged()) {
if (isDrag) {
return NetworkOperation.NULL_OPERATION_UNMANAGED;
} else {
return NetworkOperation.REMOVE_UNMANAGED_NETWORK;
}
} else {
return NetworkOperation.DETACH_NETWORK;
}
} else {
return NetworkOperation.NULL_OPERATION;
}
}
// op1 is a label, if an interface is labelled with it - unlabel
if (op1 instanceof NetworkLabelModel) {
NetworkLabelModel label = (NetworkLabelModel) op1;
if (label.isAttached()) {
return NetworkOperation.UNLABEL;
} else {
return NetworkOperation.NULL_OPERATION;
}
}
return NetworkOperation.NULL_OPERATION;
}
private static NetworkOperation handleBinaryOperation(NetworkItemModel<?> op1, NetworkItemModel<?> op2) {
// binary operation joining items together - in most cases valid iff their networks comply
if (op2 instanceof NetworkInterfaceModel) {
return binaryOperationWithNetworkInterfaceModelAsSecondOperand(op1, (NetworkInterfaceModel) op2);
}
return NetworkOperation.NULL_OPERATION;
}
private static NetworkOperation binaryOperationWithNetworkInterfaceModelAsSecondOperand(NetworkItemModel<?> op1,
NetworkInterfaceModel dst) {
// first collect the networks into one set
Set<LogicalNetworkModel> networks = new HashSet<>();
networks.addAll(dst.getItems());
// op1 is a NIC, verify that it isn't already part of a bond or dragged unto itself
if (op1 instanceof NetworkInterfaceModel) {
NetworkInterfaceModel src = (NetworkInterfaceModel) op1;
if (src.isBonded() || src.equals(dst)) {
return NetworkOperation.NULL_OPERATION;
}
networks.addAll(src.getItems());
} else if (op1 instanceof LogicalNetworkModel) {
// op1 is a network, verify that it isn't dragged unto the NIC already containing it
if (!networks.add((LogicalNetworkModel) op1)) {
return NetworkOperation.NULL_OPERATION;
}
} else if(op1 instanceof NetworkLabelModel) {
// op1 is a label, verify that it's not applied to the interface already labelled by it
NetworkLabelModel src = (NetworkLabelModel) op1;
if (dst.equals(src.getInterface())) {
return NetworkOperation.NULL_OPERATION;
}
networks.addAll(src.getNetworks());
}
// go over the networks and check whether they comply, if not - the reason is important
int nonVlanCounter = 0;
for (LogicalNetworkModel network : networks) {
if (!network.isManaged()) {
if (op1 instanceof LogicalNetworkModel) {
return NetworkOperation.NULL_OPERATION_UNMANAGED;
}
if (op1.aggregatesNetworks()) {
dst.setCulpritNetwork(network.getName());
return NetworkOperation.NULL_OPERATION_BATCH_UNMANAGED;
}
} else {
if (!network.isInSync()) {
if (op1 instanceof LogicalNetworkModel) {
return NetworkOperation.NULL_OPERATION_OUT_OF_SYNC;
}
if (op1.aggregatesNetworks()) {
dst.setCulpritNetwork(network.getName());
return NetworkOperation.NULL_OPERATION_BATCH_OUT_OF_SYNC;
}
}
}
if (!network.hasVlan()) {
++nonVlanCounter;
}
if (nonVlanCounter > 1) {
if (op1 instanceof LogicalNetworkModel) {
return NetworkOperation.NULL_OPERATION_TOO_MANY_NON_VLANS;
}
if (op1.aggregatesNetworks()) {
dst.setCulpritNetwork(network.getName());
return NetworkOperation.NULL_OPERATION_BATCH_TOO_MANY_NON_VLANS;
}
}
if (network.getNetwork().isVmNetwork()) {
if (dst instanceof BondNetworkInterfaceModel) {
BondNetworkInterfaceModel bondModel = (BondNetworkInterfaceModel) dst;
if (!BondMode.isBondModeValidForVmNetwork(bondModel.getCreateOrUpdateBond().getBondOptions())) {
return NetworkOperation.NULL_OPERATION_INVALID_BOND_MODE;
}
}
}
}
// networks comply, all that's left is to return the correct operation
if (op1 instanceof LogicalNetworkModel) {
return NetworkOperation.ATTACH_NETWORK;
}
if (op1 instanceof BondNetworkInterfaceModel) {
if (dst instanceof BondNetworkInterfaceModel) {
return NetworkOperation.JOIN_BONDS;
} else {
return NetworkOperation.EXTEND_BOND_WITH;
}
}
if (op1 instanceof NetworkInterfaceModel) {
if (dst instanceof BondNetworkInterfaceModel) {
return NetworkOperation.ADD_TO_BOND;
} else {
return NetworkOperation.BOND_WITH;
}
}
if (op1 instanceof NetworkLabelModel) {
return NetworkOperation.LABEL;
}
return NetworkOperation.NULL_OPERATION;
}
private static boolean noValidOperationForFirstOperand(NetworkItemModel<?> op1) {
// no valid operation for external networks or networks attached via label
if (op1 instanceof LogicalNetworkModel) {
LogicalNetworkModel network = (LogicalNetworkModel) op1;
if (network.getNetwork().isExternal() || network.isAttachedViaLabel()) {
return true;
}
}
return false;
}
private final List<LogicalNetworkModel> allNetworks;
private final List<NetworkInterfaceModel> nics;
/**
* Create an Operation Factory with the provided list of Networks and Nics
*/
public NetworkOperationFactory(List<LogicalNetworkModel> allNetworks, List<NetworkInterfaceModel> nics) {
this.allNetworks = allNetworks;
this.nics = nics;
}
/**
* Calculate all possible Commands for this Item, taking into account all Network Items (Nics and Networks) this
* Factory is aware of.
*/
public Map<NetworkOperation, List<NetworkCommand>> commandsFor(NetworkItemModel<?> item,
DataFromHostSetupNetworksModel dataFromHostSetupNetworksModel) {
Map<NetworkOperation, List<NetworkCommand>> operations = new HashMap<>();
// with nics
for (NetworkInterfaceModel nic : nics) {
NetworkOperation operation = operationFor(item, nic);
if (!operation.isNullOperation()) {
assertBinary(item, nic, operation);
NetworkCommand command = operation.getCommand(item,
nic,
dataFromHostSetupNetworksModel);
addToOperationMultiMap(operations, operation, command);
}
}
// with networks
for (LogicalNetworkModel network : allNetworks) {
NetworkOperation operation = operationFor(item, network);
if (!operation.isNullOperation()) {
assertBinary(item, network, operation);
NetworkCommand command = operation.getCommand(item,
network,
dataFromHostSetupNetworksModel);
addToOperationMultiMap(operations, operation, command);
}
}
// with self
NetworkOperation operation = operationFor(item, null);
if (!operation.isNullOperation()) {
assert operation.isUnary() : "Operation " + operation.name() //$NON-NLS-1$
+ " is Binary, while a Uniary Operation is expected for " + item.getName(); //$NON-NLS-1$
NetworkCommand command = operation.getCommand(item,
null,
dataFromHostSetupNetworksModel);
addToOperationMultiMap(operations, operation, command);
}
return operations;
}
private void addToOperationMultiMap(Map<NetworkOperation, List<NetworkCommand>> operationsMap,
NetworkOperation operation,
NetworkCommand command) {
List<NetworkCommand> menuItems = operationsMap.get(operation);
if (menuItems == null) {
menuItems = new ArrayList<>();
operationsMap.put(operation, menuItems);
}
menuItems.add(command);
}
private void assertBinary(NetworkItemModel<?> op1, NetworkItemModel<?> op2, NetworkOperation operation) {
assert !operation.isUnary() : "Operation " + operation.name() //$NON-NLS-1$
+ " is Unary, while a Binary Operation is expected for: " + op1.getName() + " and " + op2.getName(); //$NON-NLS-1$ //$NON-NLS-2$
}
}