/*
* Copyright (c) 2008-2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource;
import static com.emc.storageos.api.mapper.DbObjectMapper.toNamedRelatedResource;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.mapper.functions.MapNetwork;
import com.emc.storageos.api.service.impl.response.BulkList;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.DiscoveredDataObject.RegistrationStatus;
import com.emc.storageos.db.client.model.ExportGroup;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.FileShare;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.IpInterface;
import com.emc.storageos.db.client.model.Network;
import com.emc.storageos.db.client.model.NetworkSystem;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageProtocol;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.VirtualArray;
import com.emc.storageos.db.client.util.CustomQueryUtility;
import com.emc.storageos.db.client.util.EndpointUtility;
import com.emc.storageos.db.client.util.StringSetUtil;
import com.emc.storageos.db.client.util.iSCSIUtility;
import com.emc.storageos.model.BulkIdParam;
import com.emc.storageos.model.ResourceTypeEnum;
import com.emc.storageos.model.host.InitiatorList;
import com.emc.storageos.model.host.IpInterfaceList;
import com.emc.storageos.model.network.NetworkBulkRep;
import com.emc.storageos.model.pools.VirtualArrayAssignmentChanges;
import com.emc.storageos.model.ports.StoragePortList;
import com.emc.storageos.model.valid.Endpoint.EndpointType;
import com.emc.storageos.model.varray.NetworkCreate;
import com.emc.storageos.model.varray.NetworkEndpointParam;
import com.emc.storageos.model.varray.NetworkList;
import com.emc.storageos.model.varray.NetworkRestRep;
import com.emc.storageos.model.varray.NetworkUpdate;
import com.emc.storageos.networkcontroller.impl.NetworkAssociationHelper;
import com.emc.storageos.recoverpoint.utils.WwnUtils;
import com.emc.storageos.recoverpoint.utils.WwnUtils.FORMAT;
import com.emc.storageos.security.authorization.CheckPermission;
import com.emc.storageos.security.authorization.DefaultPermissions;
import com.emc.storageos.security.authorization.Role;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.util.NetworkUtil;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableBourneEvent;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEventManager;
import com.emc.storageos.volumecontroller.impl.monitoring.cim.enums.RecordType;
/**
* Network service handles requests to create, update and remove a network.
*
*/
@Path("/vdc/networks")
@DefaultPermissions(readRoles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR },
writeRoles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public class NetworkService extends TaggedResource {
private static Logger _log = LoggerFactory.getLogger(NetworkService.class);
private static final String EVENT_SERVICE_TYPE = "networks";
private static final String EVENT_SERVICE_SOURCE = "NetworksService";
@Override
public String getServiceType() {
return EVENT_SERVICE_TYPE;
}
private RecordableEventManager eventManager;
public void setEventManager(RecordableEventManager eventManager) {
this.eventManager = eventManager;
}
/**
* Get info for network
*
* @param id the URN of a ViPR Network
* @brief Show network
* @return Network details
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
public NetworkRestRep getNetwork(@PathParam("id") URI id) {
ArgValidator.checkFieldUriType(id, Network.class, "id");
return MapNetwork.toNetworkRestRep(queryResource(id), _dbClient);
}
/**
* This call returns a list of all the networks, regardless of whether or not
* they are associated with a virtual array.
* <p>
* If network systems are discovered, fiber channel networks that are discovered are not initially associated with virtual array. The
* discovered networks must be updated to associate then with virtual arrays.
*
* @brief List Networks
* @return a list of all networks
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
public NetworkList getAllNetworks(@QueryParam("wwn") String wwn) {
NetworkList tzlist = new NetworkList();
if (wwn != null) {
// Validate the argument for wwn structure...
ArgValidator.checkFieldValidWwn(wwn);
// Normalize wwn for colon-separated and all-caps
wwn = WwnUtils.convertWWN(wwn.toUpperCase(), FORMAT.COLON);
Network network = NetworkUtil.getEndpointNetwork(wwn, _dbClient);
if (network != null) {
tzlist.getNetworks().add(toNamedRelatedResource(ResourceTypeEnum.NETWORK,
network.getId(), network.getLabel()));
}
} else {
List<URI> networks = _dbClient.queryByType(Network.class, true);
List<Network> transportZones = _dbClient.queryObject(Network.class, networks);
for (Network network : transportZones) {
if (network == null || network.getInactive() == true) {
continue;
}
tzlist.getNetworks().add(toNamedRelatedResource(ResourceTypeEnum.NETWORK,
network.getId(), network.getLabel()));
}
}
return tzlist;
}
@Override
protected Network queryResource(URI id) {
ArgValidator.checkFieldUriType(id, Network.class, "id");
Network tzone = _dbClient.queryObject(Network.class, id);
ArgValidator.checkEntityNotNull(tzone, id, isIdEmbeddedInURL(id));
return tzone;
}
@Override
protected URI getTenantOwner(URI id) {
return null;
}
/**
* Deactivate network, this will delete a manually created network. The
* network must be deregistered and has no endpoints. When force is set to true
* the network can be deleted even if it has endpoints.
*
* @param id the URN of a ViPR Network
* @param force if set to true will delete a network even if it has endpoints
* @brief Delete Network
* @return No data returned in response body
*/
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/deactivate")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public Response deleteNetwork(@PathParam("id") URI id,
@QueryParam("force") boolean force) {
ArgValidator.checkFieldUriType(id, Network.class, "id");
Network network = _dbClient.queryObject(Network.class, id);
ArgValidator.checkEntityNotNull(network, id, isIdEmbeddedInURL(id));
if (!RegistrationStatus.UNREGISTERED.toString().equals(network.getRegistrationStatus())) {
throw APIException.badRequests.invalidParameterCannotDeactivateRegisteredNetwork(network.getId());
}
if (network.getDiscovered()) {
throw APIException.badRequests.invalidParameterCannotDeleteDiscoveredNetwork(network.getId());
}
if (network.getDiscovered() != true && force) {
// dis-associated the storage port from the network before marking it for deletion
NetworkAssociationHelper.handleEndpointsRemoved(network, network.retrieveEndpoints(), _dbClient, _coordinator);
_dbClient.markForDeletion(network);
} else if (network.getDiscovered() != true && network.retrieveEndpoints() != null
&& !network.retrieveEndpoints().isEmpty()) {
throw APIException.badRequests.unableToDeleteNetworkContainsEndpoints();
} else {
NetworkAssociationHelper.handleEndpointsRemoved(network, network.retrieveEndpoints(), _dbClient, _coordinator);
_dbClient.markForDeletion(network);
}
recordAndAudit(network, OperationTypeEnum.DELETE_NETWORK);
return Response.ok().build();
}
/**
* Add or remove end-point(s) to network.
* <p>
* For fiber channel, some Networks may be automatically created by discovering Network Systems. These Networks will have endpoints that
* were discovered by a Network System, including endpoints that represent host initiator port WWNs as well as end points that represent
* storage array port WWNs.
* <p>
* Discovered endpoints may not be deleted by the user. They will be updated periodically as the Network System refreshes its
* information on the topology of the VSANs or Fabrics.
* <p>
* The user may still manually add endpoints to a discovered Network. The user is able to delete endpoints that were manually added. If
* a manually entered endpoint is subsequently discovered by a a Network System, it becomes managed as if it were discovered originally,
* and then may no longer be deleted.
* <p>
* This API is maintained for backward compatibility. Since the method is deprecated use /vdc/networks/{id} instead.
*
* @see #updateNetwork(URI, NetworkUpdate)
* @param id the URN of a ViPR Network
* @param param Network endpoint parameters
* @deprecated use {@link #updateNetwork(URI, NetworkUpdate)}
* @brief Add or remove network end points
* @return Network details
*/
@PUT
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/endpoints")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
@Deprecated
public NetworkRestRep updateNetworkEndpoints(@PathParam("id") URI id,
NetworkEndpointParam param) {
Network network = doUpdateEndpoints(id, param);
recordAndAudit(network, OperationTypeEnum.UPDATE_NETWORK);
return MapNetwork.toNetworkRestRep(network, _dbClient);
}
/**
* Adds/removes endpoints to/from networks and handles all the related
* updates needed for:
* <ul>
* <li>The storage ports corresponding to the endpoints if they exist</li>
* <li>The networks, the one the endpoints are added into, and the old one when an endpoint is moving from one network to another</li>
* <li>The storage pools when this operation results in the pools being implicitly associated with the new network's varray and
* disassociated from the old ones.</li>
* </ul>
* When a port is added:
* <ul>
* <li>the port's endpoint is added to the network</li>
* <li>The port's network is set to the new network</li>
* <li>The pool-to-varray associations are updated if needed.</li>
* <li>If the ports exists in another network, its endpoint is removed from the old network and the pool-to-varray associations are also
* updated</li>
* </ul>
* When a port is removed:
* <ul>
* <li>the port's endpoint is removed from the network</li>
* <li>The port network is set null</li>
* <li>The pool-to-varray associations are updated if needed.</li>
* </ul>
*
* @param id the URN of a ViPR network
* @param param the request object containing the endpoint to be added or removed
* as well as the operation type (add/remove).
* @return the target networke
*/
public Network doUpdateEndpoints(URI id, NetworkEndpointParam param) {
// This I am not sure about -
_log.info("doUpdateEndpoints START...");
ArgValidator.checkUri(id);
Network network = _dbClient.queryObject(Network.class, id);
ArgValidator.checkEntity(network, id, isIdEmbeddedInURL(id));
NetworkEndpointParam.EndpointOp op = NetworkEndpointParam.EndpointOp.valueOf(param.getOp());
List<String> updatedEndoints = null;
// create an update parameter
if (op.equals(NetworkEndpointParam.EndpointOp.add)) {
_log.info("doUpdateEndpoints: adding endpoints {} to network {}", param.getEndpoints(), network);
updatedEndoints = checkAndFilterAddEndpoints(network, param.getEndpoints());
network.addEndpoints(updatedEndoints, false);
} else {
_log.info("doUpdateEndpoints: removing endpoints {} from network {}", param.getEndpoints(), network);
updatedEndoints = checkAndFilterRemoveEndPoints(network, param.getEndpoints());
network.removeEndpoints(updatedEndoints);
}
_dbClient.updateAndReindexObject(network);
_log.info("doUpdateEndpoints: update the port and pools associations following {} endpoints operation", op.name());
updateEndpointsAssociation(network, updatedEndoints, op);
return network;
}
/**
* check to see if any endpoint is used in an active export.
*
* @param endpoints a list of endpoints to check
*/
private void checkEndPointsForExports(Collection<String> endpoints) {
// check endpoints being added are NOT part of active exports
for (String endpoint : endpoints) {
if (StorageProtocol.isFCEndpoint(endpoint)
|| iSCSIUtility.isValidIQNPortName(endpoint)
|| iSCSIUtility.isValidEUIPortName(endpoint)) {
_log.info("checkEndPointsForExports: checking endpoint {} is not in block export", endpoint);
NetworkUtil.checkNotUsedByActiveExportGroup(endpoint, _dbClient);
} else {
NetworkUtil.checkNotUsedByActiveFileExport(endpoint, _dbClient);
_log.info("checkEndPointsForExports: checking endpoint {} is not in file export", endpoint);
}
}
}
/**
* Remove the endpoints from their current networks and update the
* the port-to-network and pool-to-varray associations as needed.
*
* @param networkMap a map containing the current network for each
* @param network the network to which the endpoints are moving
*/
private void handleRemoveFromOldNetworks(Map<String, Network> networkMap,
Network network) {
NetworkAssociationHelper.handleRemoveFromOldNetworks(networkMap, network, _dbClient, _coordinator);
}
/**
* Check endpoints being added were not discovered to be in another network
*
* @param networkMap a map containing the old network for each end point
* @param network the network to which the endpoint are moving *
*/
private void checkNotAddingDiscoveredEndpoints(Map<String, Network> networkMap, Network network) {
List<String> discoveredEndpoints = new ArrayList<String>();
_log.info("checkNotAddingDiscoveredEndpoints: checking the endpoints are not discovered in another network");
for (String ep : networkMap.keySet()) {
if (networkMap.get(ep).endpointIsDiscovered(ep) &&
!network.getId().equals(networkMap.get(ep).getId())) {
discoveredEndpoints.add(ep);
}
}
_log.info("checkNotAddingDiscoveredEndpoints: these endpoints were discovered in another network {}",
discoveredEndpoints.toArray());
if (!discoveredEndpoints.isEmpty()) {
throw APIException.badRequests.endpointsCannotBeAdded(discoveredEndpoints.toArray().toString());
}
}
/**
* Checks the endpoints being removed were not discovered to be in the network
*
* @param network Network the network from where the endpoints will be removed
* @param endpoints List of String of endpoints being removed
*/
private void checkNotRemovingDiscoveredEndpoints(Network network, List<String> endpoints) {
List<String> discoveredEndpoints = new ArrayList<String>();
_log.info("checkNotRemovingDiscoveredEndpoints: for {} ", endpoints);
for (String ep : endpoints) {
if (network.endpointIsDiscovered(ep)) {
discoveredEndpoints.add(ep);
}
}
_log.info("checkNotRemovingDiscoveredEndpoints: these endpoints were discovered in the network {} ", discoveredEndpoints);
if (!discoveredEndpoints.isEmpty()) {
throw APIException.badRequests.endpointsCannotBeRemoved(discoveredEndpoints.toArray().toString());
}
}
/**
* this updates the StoragePort to Network relationship if the endpoints represent ports,
* and then will affect StoragePool to varray associations.
*
* @param network the network where endpoints are added/removed
* @param endpoints a collection of added/removed endpoints
* @param op the type of change: added/removed
*/
private void updateEndpointsAssociation(Network network, Collection<String> endpoints,
NetworkEndpointParam.EndpointOp op) {
_log.info("updateEndpointsAssociation: update the port and pools associations following {} endpoints operation", op.name());
if (op.equals(NetworkEndpointParam.EndpointOp.add)) {
NetworkAssociationHelper.handleNetworkUpdated(network, null, null, endpoints, null, _dbClient, _coordinator);
} else {
NetworkAssociationHelper.handleNetworkUpdated(network, null, null, null, endpoints, _dbClient, _coordinator);
}
}
/**
* Create a network of type FC, IP or Ethernet. The network can optionally
* be added to varrays and populated with endpoints.
* <p>
* When the network has endpoints and the endpoints are matched to storage ports, the storage ports become assigned to the network. When
* the network is also added to virtual arrays, the storage ports' array pools are update to show they are connected to the networks'
* varrays.
*
* @param param object containing the request parameters
* @brief Create Network
* @return the details of the created network
*/
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public NetworkRestRep createNetwork(NetworkCreate param) {
_log.info("createNetwork: started for param: name {} and type {}",
param.getLabel(), param.getTransportType());
// check for active network with same name
ArgValidator.checkFieldNotEmpty(param.getLabel(), "label");
checkDuplicateLabel(Network.class, param.getLabel());
// check the type is supported
StorageProtocol.Transport type = StorageProtocol.Transport.valueOf(param.getTransportType());
// check VirtualArrays
if (param.getVarrays() != null) {
for (URI uri : param.getVarrays()) {
queryObject(VirtualArray.class, uri, true);
}
}
Network network = new Network();
network.setId(URIUtil.createId(Network.class));
network.setLabel(param.getLabel());
network.setTransportType(type.name());
network.setAssignedVirtualArrays(StringSetUtil.uriListToStringSet(param.getVarrays()));
network.addEndpoints(checkAndFilterAddEndpoints(network, param.getEndpoints()), false);
_dbClient.createObject(network);
recordAndAudit(network, OperationTypeEnum.CREATE_NETWORK);
_log.info("createNetwork: updating ports and pools associations ");
NetworkAssociationHelper.handleNetworkUpdated(network, StringSetUtil.stringSetToUriList(network.getAssignedVirtualArrays()),
null, network.getEndpointsMap().keySet(), null, _dbClient, _coordinator);
return MapNetwork.toNetworkRestRep(network, _dbClient);
}
/**
* Update a network's name, endpoints or varrays.
* <p>
* When endpoints are changed, added or removed, and the endpoints match some storage ports, the storage ports associations to the
* network are updated accordingly. If the endpoints added exist is another network, they are first removed from their current network.
* Discovered endpoints cannot be removed from their current networks or added to another one.
* <p>
* When the storage ports networks are changed, their corresponding storage pools are also update to reflect any change in varray
* connectivity that may have resulted from the change.
* <p>
* For backward compatibility, this function still allows the varray changes to be done using {@link NetworkUpdate#getVarrays()}. The
* value specified in the parameter will override the existing varrays to maintain the same behavior. Further, only zero or one varray
* may be specified using this input field.
*
* @param id the URN of a ViPR network
* @param param the update request object
* @brief Update network
* @return the details of the updated network
*/
@PUT
@Path("/{id}")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public NetworkRestRep updateNetwork(@PathParam("id") URI id,
NetworkUpdate param) {
_log.info("updateNetwork: started for network {}", id);
ArgValidator.checkFieldUriType(id, Network.class, "id");
Network network = queryResource(id);
ArgValidator.checkEntity(network, id, isIdEmbeddedInURL(id));
if (param.getName() != null && !network.getLabel().equalsIgnoreCase(
param.getName())) {
// check for active network with same name
checkDuplicateLabel(Network.class, param.getName());
_log.info("updateNetwork: changing network {} to {} ", network.getLabel(),
param.getName());
network.setLabel(param.getName());
}
if (param.getVarrays() != null && !param.getVarrays().isEmpty() && param.getVarrays().size() != 1) {
throw APIException.badRequests.networkCanOnlyBeAssociatedWithASingleVirtualArray(network.getId());
}
// define variable to hold changes
List<URI> removedVarrays = null;
List<URI> addedVarrays = null;
List<String> removedEps = null;
List<String> addedEps = null;
// Update the varray association.
if (param.getVarrayChanges() != null) {
VirtualArrayAssignmentChanges varrayChanges = param.getVarrayChanges();
if (varrayChanges.hasRemoved()) {
removedVarrays = checkAndFilterRemoveVarrays(network,
varrayChanges.getRemove().getVarrays(),
varrayChanges.hasAdded() ? varrayChanges.getAdd().getVarrays() : null);
_log.info("updateNetwork: these varrays will be removed {} ", removedVarrays);
}
if (param.getVarrayChanges().hasAdded()) {
addedVarrays = checkAndFilterAddVarrays(network, varrayChanges.getAdd().getVarrays());
_log.info("updateNetwork: these varrays will be added {} ", addedVarrays);
}
} else if (param.getVarrays() != null) {
// the user is using the old style - still allow full overwrite of the varrays
_log.info("updateNetwork: using the old style update for varrays param {}", param.getVarrays());
if (param.getVarrays().isEmpty()) {
if (network.getAssignedVirtualArrays() != null) {
removedVarrays = checkAndFilterRemoveVarrays(network,
network.getAssignedVirtualArrays(), null);
_log.info("updateNetwork: these varrays will be removed {} ", removedVarrays);
}
} else {
addedVarrays = checkAndFilterAddVarrays(network, StringSetUtil.uriListToSet(param.getVarrays()));
_log.info("updateNetwork: these varrays will be added {} ", addedVarrays);
}
}
// Update the endpoints.
if (param.getEndpointChanges() != null) {
if (param.getEndpointChanges().hasRemoved()) {
removedEps = checkAndFilterRemoveEndPoints(network, param.getEndpointChanges().getRemove());
_log.info("updateNetwork: these endpoints will be removed {} ", removedEps);
}
if (param.getEndpointChanges().hasAdded()) {
addedEps = checkAndFilterAddEndpoints(network, param.getEndpointChanges().getAdd());
_log.info("updateNetwork: these endpoints will be added {} ", addedEps);
}
}
if (removedVarrays != null) {
network.removeAssignedVirtualArrays(StringSetUtil.uriListToSet(removedVarrays));
}
if (addedVarrays != null) {
network.addAssignedVirtualArrays(StringSetUtil.uriListToSet(addedVarrays));
}
if (removedEps != null) {
network.removeEndpoints(removedEps);
}
if (addedEps != null) {
network.addEndpoints(addedEps, false);
}
_dbClient.updateAndReindexObject(network);
recordAndAudit(network, OperationTypeEnum.UPDATE_NETWORK);
_log.info("updateNetwork: updating ports and pools associations ");
NetworkAssociationHelper.handleNetworkUpdated(network, addedVarrays, removedVarrays, addedEps, removedEps, _dbClient, _coordinator);
return MapNetwork.toNetworkRestRep(network, _dbClient);
}
/**
* Validates the varrays and filter out any varrays to which the network is already assigned.
*
* @param network the network to update
* @param varrays the varrays to add
* @return a list of filtered varrays
*/
private List<URI> checkAndFilterAddVarrays(Network network, Collection<String> varrays) {
List<URI> addedVarray = new ArrayList<URI>();
URI uri = null;
for (String strUri : varrays) {
if (network.getAssignedVirtualArrays() == null ||
!network.getAssignedVirtualArrays().contains(strUri.toString())) {
uri = URI.create(strUri);
queryObject(VirtualArray.class, uri, true);
addedVarray.add(uri);
}
}
return addedVarray;
}
/**
* Validates the varrays and filter out any varrays to which the network is NOT already assigned.
* The validation ensures that the network is getting unassigned from varrays if one or more
* of the network's endpoints have exports in the varrays.
*
* @param network the network to update
* @param varrays the varrays to remove
* @return a list of filtered varrays
*/
private List<URI> checkAndFilterRemoveVarrays(Network network, Collection<String> remVarrays, Collection<String> addVarrays) {
List<URI> removedVarray = new ArrayList<URI>();
URI uri = null;
for (String strUri : remVarrays) {
if (network.getAssignedVirtualArrays() != null && // check it is already in the network
network.getAssignedVirtualArrays().contains(strUri.toString())
&& (addVarrays == null || // also check it is not in the add list
!addVarrays.contains(strUri))) {
uri = URI.create(strUri);
removedVarray.add(uri);
}
}
checkNetworkExportAssociations(network, removedVarray);
return removedVarray;
}
/**
* Validates the endpoints can be added to the network and filter out ones that are
* already in the network. The endpoints can be added to the network when:
* <ol>
* <li>The endpoint type is compatible with the network type. In other words WWN for FC networks.</li>
* <li>The endpoint is valid WWN or IQN.</li>
* <li>The endpoint is not discovered in another network</li>
* <li>The endpoint does not have any active file or block exports in their current networks</li>
* </ol>
*
* @param network the network to be updated
* @param endpoints the endpoints to be added.
* @return a list of filtered endpoints
*/
private List<String> checkAndFilterAddEndpoints(Network network, List<String> endpoints) {
List<String> addedEp = new ArrayList<String>();
if (endpoints != null) {
for (String endpoint : endpoints) {
if (network.getEndpointsMap() == null ||
!network.getEndpointsMap().containsKey(endpoint)) {
if (network.getTransportType().equals(StorageProtocol.Transport.FC.name()) &&
!EndpointUtility.isValidEndpoint(endpoint, EndpointType.WWN)) {
throw APIException.badRequests.invalidEndpointExpectedFC(endpoint);
}
if (!network.getTransportType().equals(StorageProtocol.Transport.FC.name()) &&
EndpointUtility.isValidEndpoint(endpoint, EndpointType.WWN)) {
throw APIException.badRequests.invalidEndpointExpectedNonFC(endpoint);
}
addedEp.add(endpoint);
}
}
// get the endpoints current networks as some may exist in other networks
Map<String, Network> networkMap =
NetworkAssociationHelper.getNetworksMap(addedEp, _dbClient);
// check that all the endpoints are not discovered in their current networks
checkNotAddingDiscoveredEndpoints(networkMap, network);
// check endpoints being added that were in other networks NOT part of active exports
checkEndPointsForExports(networkMap.keySet());
// remove from old networks
handleRemoveFromOldNetworks(networkMap, network);
}
return addedEp;
}
/**
* Validates the endpoints can be removed from the network and filter out ones that are
* not already in the network. The endpoints can be removed from the network when:
* <ol>
* <li>The endpoint is not discovered in the network</li>
* <li>The endpoint does not have any active file or block exports in the networks</li>
* </ol>
*
* @param network the network to be updated
* @param endpoints the endpoints to be removed.
* @return a list of filtered endpoints
*/
private List<String> checkAndFilterRemoveEndPoints(Network network, List<String> remEps) {
List<String> removedEp = new ArrayList<String>();
for (String str : remEps) {
if (network.getEndpointsMap() != null &&
network.getEndpointsMap().containsKey(EndpointUtility.changeCase(str))) {
removedEp.add(str);
}
}
// make sure the end points are not discovered in the current network
checkNotRemovingDiscoveredEndpoints(network, removedEp);
// check endpoints being removed are NOT part of active exports
checkEndPointsForExports(removedEp);
return removedEp;
}
/**
* Allows the user to deregister a registered network so that it is no
* longer used by the system. This simply sets the registration_status of
* the network to UNREGISTERED.
*
* @param id the URN of a ViPR network.
*
* @brief Unregister network
* @return Status response indicating success or failure
*/
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/deregister")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public NetworkRestRep deregisterNetwork(@PathParam("id") URI id) {
Network network = queryResource(id);
ArgValidator.checkEntity(network, id, isIdEmbeddedInURL(id));
if (RegistrationStatus.REGISTERED.toString().equalsIgnoreCase(
network.getRegistrationStatus())) {
network.setRegistrationStatus(RegistrationStatus.UNREGISTERED.toString());
_dbClient.persistObject(network);
auditOp(OperationTypeEnum.DEREGISTER_NETWORK, true, null,
network.getLabel(), network.getId().toString());
}
return MapNetwork.toNetworkRestRep(network, _dbClient);
}
/**
* Manually register the network with the passed id.
*
* @param id the URN of a ViPR network.
*
* @brief Register network
* @return A reference to a StoragePoolRestRep specifying the data for the
* registered storage pool.
*/
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
@Path("/{id}/register")
public NetworkRestRep registerNetwork(@PathParam("id") URI id) {
ArgValidator.checkFieldUriType(id, Network.class, "id");
Network network = _dbClient.queryObject(Network.class, id);
ArgValidator.checkEntity(network, id, isIdEmbeddedInURL(id));
if (RegistrationStatus.UNREGISTERED.toString().equalsIgnoreCase(
network.getRegistrationStatus())) {
if (network.getDiscovered()) {
List<URI> registeredNetworkSystems = getRegisteredNetworkSystems(network, _dbClient);
if (registeredNetworkSystems.isEmpty()) {
throw APIException.badRequests.invalidParameterCannotRegisterUnmanagedNetwork(network.getId());
}
}
network.setRegistrationStatus(RegistrationStatus.REGISTERED.toString());
_dbClient.persistObject(network);
auditOp(OperationTypeEnum.REGISTER_NETWORK, true, null, network.getId().toString());
}
return MapNetwork.toNetworkRestRep(network, _dbClient);
}
/**
* Returns the list of registered NetworkSystems that manage the given Network
*
* @param network the Network
* @param dbClient DbClient
* @return list of registered NetworkSystems that manage the given Network
*/
public static List<URI> getRegisteredNetworkSystems(Network network, DbClient dbClient) {
List<URI> networkSystems = new ArrayList<URI>();
if (network != null && network.getInactive() != true && network.getNetworkSystems() != null) {
for (String networkSystemUri : network.getNetworkSystems()) {
NetworkSystem networkSystem =
dbClient.queryObject(NetworkSystem.class, URI.create(networkSystemUri));
if (networkSystem != null
&& networkSystem.getInactive() != true
&& RegistrationStatus.REGISTERED.toString().equalsIgnoreCase(
networkSystem.getRegistrationStatus())) {
networkSystems.add(networkSystem.getId());
}
}
}
return networkSystems;
}
/**
* Retrieve resource representations based on input ids.
*
* @param param POST data containing the id list.
* @brief List data of network resources
* @return list of representations.
*/
@POST
@Path("/bulk")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public NetworkBulkRep getBulkResources(BulkIdParam param) {
return (NetworkBulkRep) super.getBulkResources(param);
}
/**
* A network varray association may have resulted in some ports being
* implicitly associated with the varray. When the network-varray association
* is being removed, we need to check that the implicitly associated ports
* are not in any exports (block or file).
*
* @param network Network to check
* @param varrayUris the list of varrays to be removed for the network
*/
private void checkNetworkExportAssociations(Network network, List<URI> varrayUris) {
// do some paramters validation
if (varrayUris == null || varrayUris.isEmpty() || network == null ||
network.getConnectedVirtualArrays() == null || network.getConnectedVirtualArrays().isEmpty()) {
return;
}
_log.info("Checking if varrays {} can be removed for network {}", varrayUris, network.getId());
// get all the storage ports in the network
List<StoragePort> storagePorts = CustomQueryUtility.queryActiveResourcesByConstraint(
_dbClient, StoragePort.class,
AlternateIdConstraint.Factory.getConstraint(
StoragePort.class, "network", network.getId().toString()));
// For each that is using implicit (connected) varray associations
// check if it is in an export
for (StoragePort storagePort : storagePorts) {
// if the port is using implicit assignment
if (storagePort != null &&
(storagePort.getAssignedVirtualArrays() == null || storagePort.getAssignedVirtualArrays().isEmpty())) {
_log.info("Port {} is using implicit varray assignment. Checking the port exports.", storagePort.getNativeGuid());
if (EndpointUtility.isValidEndpoint(storagePort.getPortNetworkId(), EndpointType.SAN)) {
_log.info("The port is of type FC or iscsi. Checking if in use by an export group.");
List<ExportMask> masks = CustomQueryUtility.queryActiveResourcesByAltId(_dbClient, ExportMask.class,
"storagePorts", storagePort.getId().toString());
if (masks != null && !masks.isEmpty()) {
_log.info("The port is in use by {} masks. Checking the masks virtual arrays.", masks.size());
for (ExportMask mask : masks) {
if (!mask.getInactive()) {
List<ExportGroup> groups = CustomQueryUtility.queryActiveResourcesByRelation(_dbClient,
mask.getId(), ExportGroup.class, "exportMasks");
for (ExportGroup group : groups) {
if (!group.getInactive() && varrayUris.contains(group.getVirtualArray())) {
_log.info("The port is in use by export group {} in virtual array {} ",
group.getLabel(), group.getVirtualArray());
throw APIException.badRequests.cannotUnassignNetworkInUse(network.getId(), group.getId(),
"ExportGroup");
}
}
}
}
}
} else {
_log.info("The port is of type IP. Checking if in use by a file share.");
List<FileShare> fileShares = CustomQueryUtility.queryActiveResourcesByRelation(
_dbClient, storagePort.getId(), FileShare.class, "storagePort");
for (FileShare fileShare : fileShares) {
if (!fileShare.getInactive() && varrayUris.contains(fileShare.getVirtualArray())) {
_log.info("The port is in use by file share {} in virtual array {} ",
fileShare.getLabel(), fileShare.getVirtualArray());
throw APIException.badRequests
.cannotUnassignNetworkInUse(network.getId(), fileShare.getId(), "FileShareExport");
}
}
}
}
}
}
/**
* This call returns a list of all Storage Ports associated
* with the Network end points.
* <p>
*
* @param id the URN of a ViPR network
* @brief List storage ports
* @return StoragePortList
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
@Path("/{id}/storage-ports")
public StoragePortList getStoragePorts(@PathParam("id") URI id) {
ArgValidator.checkUri(id);
Network tzone = _dbClient.queryObject(Network.class, id);
ArgValidator.checkEntityNotNull(tzone, id, isIdEmbeddedInURL(id));
StoragePortList registeredStoragePorts = new StoragePortList();
URIQueryResultList storagePortURIs = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getNetworkStoragePortConstraint(id.toString()),
storagePortURIs);
Iterator<URI> storagePortURIsIter = storagePortURIs.iterator();
while (storagePortURIsIter.hasNext()) {
URI storagePortURI = storagePortURIsIter.next();
StoragePort storagePort = _dbClient.queryObject(StoragePort.class,
storagePortURI);
if (storagePort != null && !storagePort.getInactive()) {
registeredStoragePorts.getPorts().add(toNamedRelatedResource(
storagePort, storagePort.getNativeGuid()));
}
}
return registeredStoragePorts;
}
/**
* This call returns a list of all Initiators associated
* with the Network end points.
*
* @param id the URN of a ViPR network
* @brief List initiators
* @return InitiatorList
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
@Path("/{id}/initiators")
public InitiatorList getInitiators(@PathParam("id") URI id) {
InitiatorList registeredInitiators = new InitiatorList();
ArgValidator.checkFieldUriType(id, Network.class, "id");
Network tzone = queryResource(id);
StringSet endpts = tzone.retrieveEndpoints();
Iterator<String> endptsIter = endpts.iterator();
URIQueryResultList resultsList = new URIQueryResultList();
while (endptsIter.hasNext()) {
String endpt = endptsIter.next();
_dbClient.queryByConstraint(
AlternateIdConstraint.Factory.getInitiatorPortInitiatorConstraint(
endpt), resultsList);
Iterator<URI> resultsIter = resultsList.iterator();
while (resultsIter.hasNext()) {
Initiator initiator = _dbClient.queryObject(
Initiator.class, resultsIter.next());
if (initiator != null) {
registeredInitiators.getInitiators().add(
toNamedRelatedResource(
initiator, initiator.getLabel()));
}
}
}
return registeredInitiators;
}
/**
* This call returns a list of all IpInterfaces associated
* with the Network end points.
*
* @param id the URN of a ViPR network
* @brief List ipInterfaces
* @return IpInterfaceList
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
@Path("/{id}/ip-interfaces")
public IpInterfaceList getIpInterfaces(@PathParam("id") URI id) {
IpInterfaceList registeredIpInterfaces = new IpInterfaceList();
ArgValidator.checkFieldUriType(id, Network.class, "id");
Network tzone = queryResource(id);
if (StorageProtocol.Transport.IP.name().equalsIgnoreCase(
tzone.getTransportType())) {
StringSet endpts = tzone.retrieveEndpoints();
Iterator<String> endptsIter = endpts.iterator();
URIQueryResultList resultsList = new URIQueryResultList();
while (endptsIter.hasNext()) {
String endpt = endptsIter.next();
_dbClient.queryByConstraint(
AlternateIdConstraint.Factory.getIpInterfaceIpAddressConstraint(
endpt.toUpperCase()), resultsList);
Iterator<URI> resultsIter = resultsList.iterator();
while (resultsIter.hasNext()) {
IpInterface ipInterface = _dbClient.queryObject(
IpInterface.class, resultsIter.next());
if (ipInterface != null) {
registeredIpInterfaces.getIpInterfaces().add(
toNamedRelatedResource(
ipInterface, ipInterface.getLabel()));
}
}
}
}
return registeredIpInterfaces;
}
@SuppressWarnings("unchecked")
@Override
public Class<Network> getResourceClass() {
return Network.class;
}
@Override
public NetworkBulkRep queryBulkResourceReps(List<URI> ids) {
Iterator<Network> _dbIterator =
_dbClient.queryIterativeObjects(getResourceClass(), ids);
return new NetworkBulkRep(BulkList.wrapping(_dbIterator, MapNetwork.getInstance()));
}
@Override
public NetworkBulkRep queryFilteredBulkResourceReps(List<URI> ids) {
verifySystemAdmin();
return queryBulkResourceReps(ids);
}
/**
* Record Bourne Event for the completed operations
*
* @param network
* @param type
* @param description
*/
private void recordNetworkEvent(Network network, String type,
String description) {
RecordableBourneEvent event = new RecordableBourneEvent(
/* String */type,
/* tenant id */null,
/* user id ?? */URI.create("ViPR-User"),
/* project ID */null,
/* CoS */null,
/* service */EVENT_SERVICE_TYPE,
/* resource id */network.getId(),
/* description */description,
/* timestamp */System.currentTimeMillis(),
/* extensions */null,
/* native guid */network.getNativeGuid(),
/* record type */RecordType.Event.name(),
/* Event Source */EVENT_SERVICE_SOURCE,
/* Operational Status codes */"",
/* Operational Status Descriptions */"");
try {
eventManager.recordEvents(event);
} catch (Exception ex) {
_log.error("Failed to record event. Event description: {}. Error: {}.",
description, ex);
}
}
private void recordAndAudit(Network network, OperationTypeEnum typeEnum) {
recordNetworkEvent(network, typeEnum.getEvType(true),
typeEnum.getDescription());
auditOp(typeEnum, true, null, network.getLabel(), network.getTransportType(),
network.getVirtualArray(), network.getId().toString());
}
@Override
protected ResourceTypeEnum getResourceType() {
return ResourceTypeEnum.NETWORK;
}
}