/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package controllers.arrays;
import com.emc.storageos.model.EndpointAliasRestRep;
import com.emc.storageos.model.host.HostRestRep;
import com.emc.storageos.model.host.InitiatorRestRep;
import com.emc.storageos.model.host.IpInterfaceRestRep;
import com.emc.storageos.model.pools.VirtualArrayAssignmentChanges;
import com.emc.storageos.model.pools.VirtualArrayAssignments;
import com.emc.storageos.model.ports.StoragePortRestRep;
import com.emc.storageos.model.ports.StoragePortUpdate;
import com.emc.storageos.model.systems.StorageSystemRestRep;
import com.emc.storageos.model.varray.NetworkCreate;
import com.emc.storageos.model.varray.NetworkRestRep;
import com.emc.storageos.model.varray.NetworkUpdate;
import com.emc.vipr.client.core.filters.ResourceFilter;
import com.emc.vipr.client.core.util.CachedResources;
import com.emc.vipr.client.core.util.ResourceUtils;
import com.emc.vipr.client.exceptions.ViPRException;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import controllers.Common;
import controllers.VirtualArrays;
import controllers.deadbolt.Restrict;
import controllers.deadbolt.Restrictions;
import controllers.util.FlashException;
import controllers.util.ViprResourceController;
import java.util.ArrayList;
import java.util.HashSet;
import models.RegistrationStatus;
import models.TransportProtocols;
import models.datatable.NetworkEndpointDataTable;
import models.datatable.NetworkEndpointDataTable.EndpointInfo;
import models.datatable.NetworksDataTable;
import models.datatable.NetworksDataTable.NetworkInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.data.binding.As;
import play.data.validation.MaxSize;
import play.data.validation.MinSize;
import play.data.validation.Required;
import play.data.validation.Validation;
import play.mvc.Util;
import play.mvc.With;
import util.HostUtils;
import util.MessagesUtils;
import util.NetworkUtils;
import util.StoragePortUtils;
import util.StorageSystemUtils;
import util.VirtualArrayUtils;
import util.datatable.DataTablesSupport;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.emc.vipr.client.core.util.ResourceUtils.uri;
import static com.emc.vipr.client.core.util.ResourceUtils.uris;
import static controllers.Common.backToReferrer;
import static controllers.Common.flashException;
import static util.BourneUtil.getViprClient;
@With(Common.class)
@Restrictions({ @Restrict("SYSTEM_ADMIN"), @Restrict("RESTRICTED_SYSTEM_ADMIN") })
public class Networks extends ViprResourceController {
protected static final String SAVED_SUCCESS = "Networks.save.success";
protected static final String SAVED_ERROR = "Networks.save.error";
protected static final String DELETED_SUCCESS = "Networks.delete.success";
protected static final String DELETED_ERROR = "Networks.delete.error";
protected static final String UNKNOWN = "Networks.unknown";
private static final String VIRTUAL_ARRAY_PARAM = "virtualArrayId";
/**
* Handles an error while saving a network form.
*
* @param network
* the network form.
*/
private static void error(NetworkForm network) {
params.flash();
Validation.keep();
edit(network.id, params.get(VIRTUAL_ARRAY_PARAM));
}
@FlashException("list")
public static void createIpNetwork(String name, String virtualArrayId) {
NetworkCreate param = new NetworkCreate();
param.setLabel(name);
param.setTransportType(TransportProtocols.IP);
if (StringUtils.isNotBlank(virtualArrayId)) {
param.setVarrays(uris(virtualArrayId));
}
NetworkRestRep network = NetworkUtils.create(param);
edit(stringId(network), virtualArrayId);
}
/**
* Displays a page for editing the given network.
*
* @param id
* the network ID.
* @param virtualArrayId
* the virtual array from which the edit request came.
*/
public static void edit(String id, String virtualArrayId) {
NetworkRestRep network = getNetwork(id);
NetworkForm form = new NetworkForm();
form.load(network);
edit(form);
}
/**
* Gets a network by ID, or flashes an error and goes back to the referring page or the list page if no referrer.
*
* @param id
* the network ID.
* @return the network.
*/
@Util
public static NetworkRestRep getNetwork(String id) {
NetworkRestRep network = NetworkUtils.getNetwork(id);
if (network == null) {
flash.error(MessagesUtils.get(UNKNOWN, id));
backToReferrer();
list();
}
return network;
}
/**
* Show the network editor.
*
* @param network
* the network to edit.
*/
private static void edit(NetworkForm network) {
renderArgs.put("virtualArrayOptions", dataObjectOptions(VirtualArrayUtils.getVirtualArrays()));
renderArgs.put(VIRTUAL_ARRAY_PARAM, params.get(VIRTUAL_ARRAY_PARAM));
renderArgs.put("dataTable", NetworkEndpointDataTable.createDataTable(network.type));
render("@edit", network);
}
public static void getConnectedStorage() {
List<NetworkRestRep> networks = NetworkUtils.getNetworks();
Set<String> connectedstoragesystems = new HashSet<String>();
for (NetworkRestRep network:networks) {
for(StoragePortRestRep port:StoragePortUtils.getStoragePortsByNetwork(network.getId())){
connectedstoragesystems.add(port.getStorageDevice().getId().toString());
}
}
renderJSON(connectedstoragesystems);
}
public static void getDisconnectedStorage(@As(",") String[] ids) {
List<NetworkRestRep> networks = NetworkUtils.getNetworks();
Set<String> connectedstoragesystems = new HashSet<String>();
Set<String> disConnectedstoragesystems = new HashSet<String>();
for (NetworkRestRep network:networks) {
for(StoragePortRestRep port:StoragePortUtils.getStoragePortsByNetwork(network.getId())){
connectedstoragesystems.add(port.getStorageDevice().getId().toString());
}
}
for (String id:ids) {
StorageSystemRestRep storageSystem = StorageSystemUtils
.getStorageSystem(id);
if (storageSystem == null || storageSystem.getRegistrationStatus().equals("UNREGISTERED")) {
//ignore for now
continue;
}
if (!connectedstoragesystems.contains(id)){
disConnectedstoragesystems.add(storageSystem.getName());
}
}
renderJSON(disConnectedstoragesystems);
}
/**
* Creates and renders JSON datatable source for endpoints for the given network.
*
* @param networkId
* the network ID.
*/
public static void endpointsJson(String networkId) {
NetworkRestRep network = NetworkUtils.getNetwork(networkId);
if (network == null) {
error(MessagesUtils.get(UNKNOWN, networkId));
}
List<EndpointInfo> items = Lists.newArrayList();
// All known endpoints, remove endpoints from storage systems and hosts to get manual endpoints
Set<String> endpoints = Sets.newTreeSet(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2);
}
});
endpoints.addAll(network.getEndpoints());
// Add ports from storage systems
CachedResources<StorageSystemRestRep> storageSystems = StorageSystemUtils.createCache();
for (StoragePortRestRep storagePort : StoragePortUtils.getStoragePortsByNetwork(network.getId())) {
items.add(new EndpointInfo(storagePort, storageSystems));
for (String endpoint : NetworkUtils.getEndPoints(storagePort)) {
endpoints.remove(endpoint);
}
}
// Add ports from hosts
CachedResources<HostRestRep> hosts = HostUtils.createCache();
for (InitiatorRestRep initiator : NetworkUtils.getInitiators(network.getId())) {
if (initiator.getHost() != null) {
items.add(new EndpointInfo(initiator, hosts));
endpoints.remove(NetworkUtils.getEndPoint(initiator));
}
}
for (IpInterfaceRestRep ipInterface : NetworkUtils.getIpInterfaces(network.getId())) {
if (ipInterface.getHost() != null) {
items.add(new EndpointInfo(ipInterface, hosts));
endpoints.remove(NetworkUtils.getEndPoint(ipInterface));
}
}
// Add any remaining endpoints as 'manual'
for (String endpoint : endpoints) {
items.add(new EndpointInfo(endpoint));
}
setEndpointAttrs(network, items);
renderJSON(DataTablesSupport.createJSON(items, params));
}
private static void setEndpointAttrs(NetworkRestRep network, List<EndpointInfo> endpoints) {
Set<String> discovered = NetworkUtils.getDiscoveredEndpoints(network);
Map<String, String> aliases = NetworkUtils.getAliases(network);
for (EndpointInfo endpoint : endpoints) {
endpoint.discovered = discovered.contains(endpoint.identifier);
endpoint.alias = aliases.get(endpoint.identifier);
}
}
/**
* Saves a network and redirects back to the referring page.
*
* @param network
* the network to save.
*/
public static void save(NetworkForm network) {
network.validate("network");
if (Validation.hasErrors()) {
error(network);
}
try {
network.save();
flash.success(MessagesUtils.get(SAVED_SUCCESS, network.name));
String virtualArrayId = params.get(VIRTUAL_ARRAY_PARAM);
if (StringUtils.isNotBlank(virtualArrayId)) {
VirtualArrays.networks(virtualArrayId);
}
backToReferrer();
list();
} catch (ViPRException e) {
flashException(e);
error(network);
}
}
/**
* Lists all networks.
*/
public static void list() {
NetworksDataTable dataTable = new NetworksDataTable();
render(dataTable);
}
/**
* Retrieves all networks and renders them as JSON for a datatable.
*/
public static void listJson() {
// Creates a mapping of ID => virtual array name
Map<URI, String> virtualArrays = ResourceUtils.mapNames(getViprClient().varrays().list());
List<NetworkInfo> items = Lists.newArrayList();
for (NetworkRestRep network : NetworkUtils.getNetworks()) {
NetworkInfo info = new NetworkInfo(network);
Set<String> varrayNames = getNames(virtualArrays, uris(network.getAssignedVirtualArrays()));
info.virtualArrayNames = StringUtils.join(varrayNames, ",");
items.add(info);
}
renderJSON(DataTablesSupport.createJSON(items, params));
}
/**
* Gets the set of names for the given IDs by looking up the values in the
* map.
*
* @param nameMap
* the map of ID->name
* @param ids
* the collection of IDs.
* @return the set of names.
*/
private static Set<String> getNames(Map<URI, String> nameMap, Collection<URI> ids) {
Set<String> names = Sets.newTreeSet();
for (URI id : ids) {
String name = nameMap.get(id);
if (StringUtils.isNotBlank(name)) {
names.add(name);
}
}
return names;
}
/**
* Deletes/deregisters the specified networks.
*
* @param ids
* the IDs of the virtual arrays to delete/deregister.
*/
public static void delete(@As(",") String[] ids) {
delete(uris(ids));
}
/**
* Deletes/deregisters the specified networks and redirects back to the list page.
*
* @param ids
* the list of IDs.
*/
private static void delete(List<URI> ids) {
performSuccessFail(NetworkUtils.getNetworks(ids), new DeactivateOperation(), DELETED_SUCCESS, DELETED_ERROR);
list();
}
public static void register(@As(",") String[] ids) {
registerNetworks(uris(ids));
list();
}
@Util
public static void registerNetworks(List<URI> ids) {
if ((ids != null) && (!ids.isEmpty())) {
for (NetworkRestRep network : NetworkUtils.getNetworks(ids)) {
if (RegistrationStatus.isUnregistered(network.getRegistrationStatus())) {
NetworkUtils.register(network.getId());
}
}
}
}
public static void deregister(@As(",") String[] ids) {
deregisterNetworks(uris(ids));
list();
}
@Util
public static void deregisterNetworks(List<URI> ids) {
if ((ids != null) && (!ids.isEmpty())) {
for (NetworkRestRep network : NetworkUtils.getNetworks(ids)) {
if (RegistrationStatus.isRegistered(network.getRegistrationStatus())) {
NetworkUtils.deregister(network.getId());
}
}
}
}
public static void removePorts(String id, @As(",") String[] ids) {
try {
if (ids != null && ids.length > 0) {
String[] decodeIds = decodeIds(ids);
if (decodeIds.length > 0) {
List<String> hostEndpoints = NetworkUtils.getHostEndpoints(Arrays.asList(decodeIds));
if (!hostEndpoints.isEmpty()) {
NetworkUtils.removeEndpoints(id, hostEndpoints);
}
for (String storagePortId : NetworkUtils.getStoragePortEndpoints(Arrays.asList(decodeIds))) {
StoragePortUtils.unassign(storagePortId);
}
}
}
} catch (Exception e) {
flashException(e);
}
edit(id, params.get(VIRTUAL_ARRAY_PARAM));
}
private static String[] decodeIds(String[] ids) throws UnsupportedEncodingException {
String[] decodedIds = new String[0];
if (ids != null && ids.length > 0) {
decodedIds = new String[ids.length];
for (int i = 0; i < ids.length; i++) {
decodedIds[i] = URLDecoder.decode(ids[i], "UTF-8");
}
}
return decodedIds;
}
@FlashException(referrer = { "edit" })
public static void addPorts(String id, String ports) {
if (StringUtils.isNotBlank(ports)) {
String[] values = ports.replaceAll("\r\n", "\n").split("\n");
Set<String> endpoints = Sets.newHashSet();
Set<String> invalidEndpoints = Sets.newHashSet();
for (String value : values) {
if (StringUtils.isNotBlank(value)) {
if (StringUtils.contains(value, ',')) {
invalidEndpoints.add(value);
} else {
endpoints.add(value);
}
}
}
if (!endpoints.isEmpty()) {
NetworkUtils.addEndpoints(id, endpoints);
}
if (!invalidEndpoints.isEmpty()) {
flash.error(MessagesUtils.get("network.ports.add.error.invalid", invalidEndpoints));
}
}
edit(id, params.get(VIRTUAL_ARRAY_PARAM));
}
@FlashException(referrer = { "edit" })
public static void addHostPorts(String id, @As(",") String[] ids) {
if (ids != null) {
List<String> endpoints = NetworkUtils.getHostEndpoints(Arrays.asList(ids));
if (!endpoints.isEmpty()) {
NetworkUtils.addEndpoints(id, endpoints);
}
}
edit(id, params.get(VIRTUAL_ARRAY_PARAM));
}
@FlashException(referrer = { "edit" })
public static void addArrayPorts(String id, @As(",") String[] ids) {
if (ids != null) {
StoragePortUpdate update = new StoragePortUpdate();
update.setNetwork(uri(id));
for (String storagePortId : NetworkUtils.getStoragePortEndpoints(Arrays.asList(ids))) {
StoragePortUtils.update(uri(storagePortId), update);
}
}
edit(id, params.get(VIRTUAL_ARRAY_PARAM));
}
public static void availableHostEndpointsJson(String id) {
NetworkRestRep network = NetworkUtils.getNetwork(id);
CachedResources<HostRestRep> hosts = HostUtils.createCache();
List<EndpointInfo> items = Lists.newArrayList();
if (TransportProtocols.isIp(network.getTransportType())) {
// Host IP interfaces not in the network
for (IpInterfaceRestRep ipInterface : NetworkUtils.getEligibleIpInterfaces(network.getId())) {
items.add(new EndpointInfo(ipInterface, hosts));
}
}
// Host initiators not in the network
Set<String> protocols = NetworkUtils.getSupportedProtocols(network);
for (InitiatorRestRep initiator : NetworkUtils.getEligibleInitiators(network.getId(), protocols)) {
items.add(new EndpointInfo(initiator, hosts));
}
setEndpointAttrs(network, items);
renderJSON(DataTablesSupport.createJSON(items, params));
}
public static void availableArrayEndpointsJson(String id) {
// Get the ports NOT in the specified network matching the network type
NetworkRestRep network = NetworkUtils.getNetwork(id);
StoragePortUtils.TransportTypeFilter transportTypeFilter = new StoragePortUtils.TransportTypeFilter(
network.getTransportType());
ResourceFilter<StoragePortRestRep> notInNetworkFilter = new StoragePortUtils.NetworkFilter(uri(id)).not();
listArrayEndpointsJson(network, StoragePortUtils.getStoragePorts(transportTypeFilter.and(notInNetworkFilter)));
}
private static void listArrayEndpointsJson(NetworkRestRep network, List<StoragePortRestRep> ports) {
CachedResources<StorageSystemRestRep> storageSystems = StorageSystemUtils.createCache();
List<EndpointInfo> items = Lists.newArrayList();
for (StoragePortRestRep port : ports) {
items.add(new EndpointInfo(port, storageSystems));
}
setEndpointAttrs(network, items);
renderJSON(DataTablesSupport.createJSON(items, params));
}
protected static class DeactivateOperation implements ResourceValueOperation<Void, NetworkRestRep> {
@Override
public Void performOperation(NetworkRestRep network) throws Exception {
boolean registered = RegistrationStatus.isRegistered(network.getRegistrationStatus());
if (registered) {
NetworkUtils.deregister(network.getId());
}
// Only delete networks that are not discovered
try {
if (Boolean.FALSE.equals(network.getDiscovered())) {
NetworkUtils.deactivate(network.getId());
}
} catch (Exception e) {
// If deactivate failed, re-register the network
if (registered) {
NetworkUtils.register(network.getId());
}
throw e;
}
return null;
}
}
/**
* Network create/edit form.
*/
public static class NetworkForm {
public String id;
public Set<String> virtualArrays;
@MaxSize(128)
@MinSize(2)
@Required
public String name;
@Required
public String type;
public String fabricId;
public boolean discovered;
public boolean isNew() {
return StringUtils.isBlank(id);
}
public void load(NetworkRestRep network) {
this.id = stringId(network);
this.virtualArrays = network.getAssignedVirtualArrays();
this.name = network.getName();
this.type = network.getTransportType();
this.fabricId = network.getFabricId();
this.discovered = Boolean.TRUE.equals(network.getDiscovered());
}
public NetworkRestRep create() {
NetworkCreate create = new NetworkCreate();
create.setLabel(name);
create.setTransportType(type);
create.setVarrays(uris(virtualArrays));
return NetworkUtils.create(create);
}
public NetworkRestRep update() {
NetworkRestRep oldNetwork = NetworkUtils.getNetwork(id);
NetworkUpdate update = new NetworkUpdate();
update.setName(name);
update.setVarrayChanges(getVirtualArrayChanges(oldNetwork));
return NetworkUtils.update(uri(id), update);
}
private VirtualArrayAssignmentChanges getVirtualArrayChanges(NetworkRestRep oldNetwork) {
Set<String> added = getAddedVirtualArrays(oldNetwork);
Set<String> removed = getRemovedVirtualArrays(oldNetwork);
VirtualArrayAssignmentChanges changes = new VirtualArrayAssignmentChanges();
if (!added.isEmpty()) {
changes.setAdd(new VirtualArrayAssignments(added));
}
if (!removed.isEmpty()) {
changes.setRemove(new VirtualArrayAssignments(removed));
}
return changes;
}
@SuppressWarnings("unchecked")
private Set<String> getAddedVirtualArrays(NetworkRestRep oldNetwork) {
Set<String> oldVirtualArrays = defaultSet(oldNetwork.getAssignedVirtualArrays());
Set<String> newVirtualArrays = defaultSet(virtualArrays);
return Sets.newHashSet(CollectionUtils.subtract(newVirtualArrays, oldVirtualArrays));
}
@SuppressWarnings("unchecked")
private Set<String> getRemovedVirtualArrays(NetworkRestRep oldNetwork) {
Set<String> oldVirtualArrays = defaultSet(oldNetwork.getAssignedVirtualArrays());
Set<String> newVirtualArrays = defaultSet(virtualArrays);
return Sets.newHashSet(CollectionUtils.subtract(oldVirtualArrays, newVirtualArrays));
}
private static <T> Set<T> defaultSet(Set<T> set) {
if (set == null) {
set = Collections.emptySet();
}
return set;
}
public NetworkRestRep save() {
if (isNew()) {
return create();
}
else {
return update();
}
}
public void validate(String fieldName) {
Validation.valid(fieldName, this);
}
}
}