/**
  * Remove the transport zone for a given network system. This typically means to dis-associated it
  * unless this is the last network system associated with the transport zone. In this case, the
  * transport zone will be deleted if:
  *
  * <ul>
  *   <li>It was discovered
  *   <li>It does not have any user-created ports
  *   <li>It does not have any registered ports
  * </ul>
  *
  * @param tzone
  * @param uri
  * @throws IOException
  */
 public List<String> removeNetworkSystemTransportZone(Network tzone, String uri)
     throws IOException {
   tzone.removeNetworkSystems(Collections.singletonList(uri)); // dis-associate
   // list of end points getting deleted
   ArrayList<String> toRemove = new ArrayList<String>();
   if (tzone.getNetworkSystems().isEmpty()) { // if this is the last network system
     List<String> userCreatedEndPoints = TransportZoneReconciler.getUserCreatedEndPoints(tzone);
     if (userCreatedEndPoints.isEmpty()
         && !tzone.assignedToVarray()) { // delete only if not modified by a user
       _log.info("Removing network {}", tzone.getLabel());
       toRemove.addAll(tzone.retrieveEndpoints());
       NetworkAssociationHelper.handleEndpointsRemoved(tzone, toRemove, dbClient, _coordinator);
       dbClient.markForDeletion(tzone);
       recordTransportZoneEvent(
           tzone,
           OperationTypeEnum.DELETE_NETWORK.getEvType(true),
           OperationTypeEnum.DELETE_NETWORK.getDescription());
     } else {
       _log.info(
           "Network {} is changed by the user and will "
               + "not be removed. Discovered end points will be removed.",
           tzone.getLabel());
       for (String pt : tzone.retrieveEndpoints()) {
         if (!userCreatedEndPoints.contains(pt)) {
           toRemove.add(pt);
         }
       }
       tzone.removeEndpoints(toRemove);
       NetworkAssociationHelper.handleEndpointsRemoved(tzone, toRemove, dbClient, _coordinator);
       _log.info("Discovered endpoints removed {}", toRemove.toArray());
       dbClient.persistObject(tzone);
       recordTransportZoneEvent(
           tzone,
           OperationTypeEnum.UPDATE_NETWORK.getEvType(true),
           OperationTypeEnum.UPDATE_NETWORK.getDescription());
     }
   } else {
     _log.info("Removing network {} from network system {}", tzone.getLabel(), uri);
     dbClient.persistObject(tzone);
     recordTransportZoneEvent(
         tzone,
         OperationTypeEnum.UPDATE_NETWORK.getEvType(true),
         OperationTypeEnum.UPDATE_NETWORK.getDescription());
   }
   return toRemove;
 }
  /**
   * Given the updated list of end points for one network system, this function will update the
   * transport zones. Require lock when reconciles vsan in fabrics that are linked through ISL.
   * Without locking, multiple VSANs could have same native gui id within the same fabric.
   *
   * @param networkSystem the network system
   * @param routedEndpoints IN/OUT parameter to get the routed endpoints map of
   *     Fabric-WWN-to-endpoints-WWN
   * @throws ControllerException
   */
  private void reconcileTransportZones(
      NetworkSystem networkSystem, Map<String, Set<String>> routedEndpoints)
      throws ControllerException {

    _log.info("reconcileTransportZones for networkSystem {}", networkSystem.getId());
    ControllerServiceImpl.Lock lock =
        ControllerServiceImpl.Lock.getLock(ControllerServiceImpl.DISCOVERY_RECONCILE_TZ);
    try {
      _log.debug(
          "Acquiring lock to reconcile transport zone for networkSystem {}", networkSystem.getId());
      lock.acquire();
      _log.info(
          "Acquired lock to reconcile transport zone for networkSystem {}", networkSystem.getId());

      // get the network system's connections from the database
      Iterator<FCEndpoint> iNewEndPoints = getNetworkSystemEndPoints(networkSystem);
      // get all the transport zones we have in the DB
      List<Network> oldTransportZones = getCurrentTransportZones();
      _log.info("Found {} existing transport zones", oldTransportZones.size());
      // get the fabrics that exist on the network system
      Map<String, String> fabricIdsMap = getDevice().getFabricIdsMap(networkSystem);
      // get the list of fabrics added, removed, changed
      TransportZoneReconciler reconciler = new TransportZoneReconciler();
      TransportZoneReconciler.Results results =
          reconciler.reconcile(networkSystem, iNewEndPoints, fabricIdsMap, oldTransportZones);
      String networkSystemUri = networkSystem.getId().toString();
      for (Network tzone : results.getRemoved()) {
        List<String> removedEps = removeNetworkSystemTransportZone(tzone, networkSystemUri);
        _log.info(
            "Removed network {} which removed discovered endpoints {}",
            tzone.getNativeGuid(),
            removedEps);
      }
      for (Network tzone : results.getAdded()) {
        handleEndpointsAdded(tzone, tzone.retrieveEndpoints());
        saveTransportZone(tzone, true);
      }
      for (Network tzone : results.getModified()) {
        if (results.getRemovedEndPoints().get(tzone) != null) {
          NetworkAssociationHelper.handleEndpointsRemoved(
              tzone, results.getRemovedEndPoints().get(tzone), dbClient, _coordinator);
        }
        if (results.getAddedEndPoints().get(tzone) != null) {
          handleEndpointsAdded(tzone, results.getAddedEndPoints().get(tzone));
        }
        saveTransportZone(tzone, false);
      }
      // update routed networks for routed and modified networks
      updateRoutedNetworks(networkSystem, results.getAddedAndModified(), routedEndpoints);
    } catch (Exception ex) {
      throw NetworkDeviceControllerException.exceptions.reconcileTransportZonesFailedExc(
          new Date().toString(), ex);
    } finally {
      try {
        _log.debug(
            "Releasing reconcile transport zone lock for networkSystem {}", networkSystem.getId());
        lock.release();
        _log.info(
            "Released reconcile transport zone lock for networkSystem {}", networkSystem.getId());
      } catch (Exception e) {
        _log.error(
            "Failed to release  Lock while reconcile transport zone for network {} -->{}",
            networkSystem.getId(),
            e.getMessage());
      }
    }
  }