/**
   * syncProbe() Called during incremental discovery Rediscover and parse the load balancing virtual
   * server object that is represented by the given VirtualApplication object
   *
   * @param objects List of existing VirtualApplication objects to be rediscovered
   */
  private void syncProbe(EList<Object> objects) {
    if (!FeaturesManager.vmtMANAGER.isLoadBalancerEnabled()) {
      logger.error("Operations Manager is not licensed for Load Balancer targets");
      return;
    }
    if (!objects.isEmpty() && initProbe() != null) {
      if (logPrefix == "") logPrefix = target.getNameOrAddress() + " : ";
      for (Object object : objects) {
        if (!(object instanceof VirtualApplication)) continue;
        // Get all the lbv servers from NetScaler appliance
        VirtualApplication vApp = (VirtualApplication) object;
        DiscoveryExtension discExt = vApp.getDiscoveryExtension();
        if (logger.isDebugEnabled())
          logger.debug(logPrefix + " rediscovering virtual application " + vApp.toVMTString());

        if (discExt instanceof NetScalerVirtualAppDiscExt) { // lbvserver
          lbvserver server = NetScalerUtil.getLbvServer(session, vApp.getName(), target, logPrefix);
          if (server != null) parseServer(server);
          else exceptions = true;
        } else if (discExt instanceof NetScalerGslbVirtualAppDiscExt) { // gslb vserver
          gslbvserver gslb =
              NetScalerUtil.getGslbvServer(session, vApp.getName(), target, logPrefix);
          if (gslb != null) parseGslbServer(gslb);
          else exceptions = true;
        }
      }
      // If no exceptions clear previous occurrences of DISCOVERY notifications
      if (!exceptions) target.clearNotifications(NotificationCategory.DISCOVERY);
    }
  }
  /**
   * Set up properties for the given virtual application object representing a NetScaler loading
   * balance. and its discovery extension. Create the GslbServiceAgent to capture the lbvserver and
   * app services that are configured on this target and are bound to the specified GSLB. Also
   * create the transaction commodity sold by the vApp.
   *
   * @param gslb VirtualApplication for the load balancing server
   * @param specificTarget NetScaler appliance target
   * @param vServerExt NetScalerGslbVirtualAppDiscExt Discovery extension for the virtual
   *     application
   * @param localName gslb server name to be set as local name in the discovery extension
   * @param vAppUuid UUID created for the virtual application
   * @param vAppType load balancing service protocol
   * @param classname static string "gslbvserver" to set as class name in the discovery extension
   * @param lbvServers list of lbvservers configured on the gslb on this target
   * @return modified vApp object
   */
  private VirtualApplication createGslbVirtualApplication(
      VirtualApplication gslb,
      VMTTarget specificTarget,
      NetScalerGslbVirtualAppDiscExt vServerExt,
      String localName,
      String vAppUuid,
      String vAppType,
      String classname,
      List<String> lbvServers) {
    // set properties in the virtual app and the discovery extension
    // and create the transaction commodity sold
    gslb =
        createVirtualApplication(
            gslb,
            specificTarget,
            vServerExt,
            localName, // service name as the local name
            "", // there is no IP and port associated with the GSLB
            0, // there is no port for the GSLB service
            vAppUuid, // uuid for the gslb vapp containing its service name only
            vAppType, // service type
            NetScalerUtil.GSLB_VSERVER // class name for the poller
            );

    // agent to represent this service on this target
    String agentName =
        NetScalerUtil.GSLB_AGENT_PREFIX + gslb.getName() + "-" + target.getNameOrAddress();
    GslbServiceAgent lbAgent =
        (GslbServiceAgent)
            createObject(MediationPackage.eINSTANCE.getGslbServiceAgent(), agentName);
    lbAgent.setDisplayName(gslb.getName() + "-" + target.getNameOrAddress());
    // discovery extension for the agent
    NetScalerGslbServiceAgentDiscExt agentDiscExt =
        (NetScalerGslbServiceAgentDiscExt)
            lbAgent.createExtension(
                DiscoveryExtensionsPackage.eINSTANCE.getNetScalerGslbServiceAgentDiscExt());
    // we set the GSLB server name as the local name
    setExtProps(
        agentDiscExt,
        specificTarget,
        NetScalerUtil.LB_VSERVER, // class name attribute in the extension used during polling
        agentName, // display name attribute in the extension
        localName, // local name attribute
        localName, // id attribute
        logPrefix);

    // attach to the vApp
    gslb.getAgents().add(lbAgent);
    agentDiscExt.getLbvServers().addAll(lbvServers);
    if (logger.isDebugEnabled())
      logger.debug(
          logPrefix
              + gslb.toVMTString()
              + " : create glsb agent on target "
              + target.getNameOrAddress());

    return gslb;
  }
  /**
   * syncProbe - main probe Get all the load balancing virtual server objects from NetScaler and
   * parse them
   */
  private void syncProbe() {
    if (!FeaturesManager.vmtMANAGER.isLoadBalancerEnabled()) {
      logger.error("Operations Manager is not licensed for Load Balancer targets");
      return;
    }
    if (initProbe() != null) {
      if (logPrefix == "") logPrefix = target.getNameOrAddress() + " : ";
      // --------- global load balancing servers
      // The gslb virtual servers correspond to gslb virtual server applications
      // Get all the gslb vservers from NetScaler appliance
      gslbvserver[] gslbvServers = NetScalerUtil.getAllGslbvServers(session, target, logPrefix);

      if (gslbvServers != null) {
        logger.info(
            logPrefix
                + "Retrieved "
                + gslbvServers.length
                + " instances of GSLB Virtual Application");

        // Parse each server object
        for (gslbvserver vServer : gslbvServers) {
          VirtualApplication vApp = parseGslbServer(vServer);
          if (logger.isDebugEnabled())
            logger.debug(logPrefix + " discovered GSLB virtual application " + vApp.toVMTString());
        }
      } else {
        exceptions = true;
      }

      // -------------- lb vservers
      // The lbv servers correspond to virtual load balancing virtual server applications
      // Get all the lbv servers from NetScaler appliance
      lbvserver[] vServers = NetScalerUtil.getAllLbvServers(session, target, logPrefix);

      if (vServers != null) {
        logger.info(
            logPrefix + "Retrieved " + vServers.length + " instances of Virtual Application");

        // Parse each server object
        for (lbvserver vServer : vServers) {
          VirtualApplication vApp = parseServer(vServer);
          if (vApp != null && logger.isDebugEnabled())
            logger.debug(logPrefix + " discovered virtual application " + vApp.toVMTString());
        }
      } else {
        exceptions = true;
      }

      // If no exceptions clear previous occurrences of DISCOVERY notifications
      if (!exceptions) target.clearNotifications(NotificationCategory.DISCOVERY);
    }
  } // end syncProbe()
  /**
   * Update the Gslb Agent attached to the Gslb application that the lbvserver is bound to
   *
   * @param vServerIP
   */
  private void updateGslbVAppAgent(String vServerIP, Integer vServerPort) {

    // the gslb vApp that this lbvserver is bound to
    VirtualApplication gslb = lbvServerVIPToGslbVAppMap.get(vServerIP);
    // if (logger.isDebugEnabled())
    logger.info(
        logPrefix
            + "LB virtual server at VIP "
            + vServerIP
            + " on target "
            + target.getNameOrAddress()
            + " is bound to  GSLB "
            + gslb.toVMTString());
    // port for the GSLB service
    gslb.setPort(vServerPort);

    // gslb agent for this target
    GslbServiceAgent lbAgent = NetScalerUtil.getTargetAgent(gslb, target);
    if (lbAgent == null) {
      logger.warn("null gslb service agent for gslb " + gslb.toVMTString());
      // TODO: create it ?
    }
    NetScalerGslbServiceAgentDiscExt lbAgentDiscExt =
        (NetScalerGslbServiceAgentDiscExt) lbAgent.getDiscoveryExtension();
    Map<String, String> vipToServiceName = lbAgentDiscExt.getVipToLbServiceName();
    if (vipToServiceName == null) {
      vipToServiceName = new HashMap<String, String>();
      lbAgentDiscExt.setVipToLbServiceName(vipToServiceName);
    }
    vipToServiceName.put(vServerIP, lbvserver_ip2serviceName.get(vServerIP));

    Map<String, Map<String, String>> vipToAppServices = lbAgentDiscExt.getVipToAppServices();
    if (vipToAppServices == null) {
      vipToAppServices = new HashMap<String, Map<String, String>>();
      lbAgentDiscExt.setVipToAppServices(vipToAppServices);
    }
    vipToAppServices.put(vServerIP, ip2NameIPPort);

    if (logger.isDebugEnabled()) {
      logger.debug(
          logPrefix
              + " added vipToServiceName Map : "
              + lbAgentDiscExt.getVipToLbServiceName()
              + " for "
              + lbAgent.toVMTString());
      logger.debug(
          logPrefix
              + " added vipToAppServices Map : "
              + lbAgentDiscExt.getVipToAppServices()
              + " for "
              + lbAgent.toVMTString());
    }

    // create IP objects for the app service endpoints.
    for (String ip : ip2NameIPPort.keySet()) {
      IP serviceIP = DiscoveryUtil.createOrGetIPObj(ip, logPrefix);
      if (logger.isDebugEnabled())
        logger.debug(
            target.getNameOrAddress()
                + " created IP : "
                + serviceIP.toVMTString()
                + " for "
                + ip
                + " :: Name/IP/Port : "
                + ip2NameIPPort.get(ip));
    }
  }
  /**
   * Global load balancing virtual server and its bound services
   *
   * @param vServer
   * @return
   */
  private VirtualApplication parseGslbServer(gslbvserver vServer) {
    if (logger.isTraceEnabled()) logger.trace(logPrefix + vServer);

    // Retrieve the gslb virtual server information
    String vServerName, vServerUuid, vServerType;
    try {
      vServerName =
          vServer.get_name(); // name of the loading balancing virtual server, is changeable
      vServerUuid = "VirtualApplicationUuid" + "_" + vServerName; // create UUID, no target address
      // because the same service could be configured on multiple targets
      vServerType =
          vServer.get_servicetype(); // Protocol used by the service (also called the service type)
      if (logger.isDebugEnabled())
        logger.debug(
            logPrefix
                + "GSLB Load Balancing Server service name "
                + vServerName
                + "::service type "
                + vServerType
                + "::uuid "
                + vServerUuid);
    } catch (Exception e) {
      exceptions = true;
      logger.error(
          logPrefix
              + "Failed retrieving gslb virtual server information on target "
              + target.getNameOrAddress(),
          e);
      return null;
    }

    // Get the binding objects for the services bound to each load balancer virtual server on the
    // appliance
    gslbvserver_gslbservice_binding[] bindObjs =
        NetScalerUtil.getBoundGslbServices(session, vServerName, target, logPrefix);
    if (logger.isTraceEnabled()) {
      int length = (bindObjs == null) ? 0 : bindObjs.length;
      logger.trace(logPrefix + "GSLB Server " + vServerName + " has " + length + " bound services");
    }

    List<String> lbvServers = new ArrayList<String>();
    if (bindObjs != null) {
      try {
        for (gslbvserver_gslbservice_binding svcBinding : bindObjs) {
          // save all the  lb virtual server's ip addresses configured in the gslbvserver in this
          // list
          // we will encounter the local lb virtual server while parsing the lbvServers in the
          // target
          // VIP address of the load balancing virtual server
          lbvServers.add(svcBinding.get_ipaddress());
        }
      } catch (Exception ex) {
        exceptions = true;
        logger.error(
            logPrefix + "Failed retrieving  information for services bound to gslb " + vServerName,
            ex);
        ex.printStackTrace();
      }
    }
    if (logger.isDebugEnabled())
      logger.debug(
          logPrefix
              + "GSLB Server "
              + vServerName
              + " has these local LB virtual server VIPs "
              + lbvServers);

    VirtualApplication gslb;
    NetScalerGslbVirtualAppDiscExt vServerExt;
    synchronized (REPOSMAN) {
      // Create the VirtualApplication object
      gslb =
          (VirtualApplication)
              createObject(
                  AbstractionPackage.eINSTANCE.getVirtualApplication(),
                  NetScalerUtil.VAPP_PREFIX + vServerName,
                  vServerUuid);
      // create VirtualApp Discovery extension
      vServerExt =
          (NetScalerGslbVirtualAppDiscExt)
              gslb.createExtension(
                  DiscoveryExtensionsPackage.eINSTANCE.getNetScalerGslbVirtualAppDiscExt());

      for (String vserver :
          lbvServers) { // add instead of allAll, so the ones added by another target are not
                        // overwritten
        if (!vServerExt.getLbvServers().contains(vserver)) vServerExt.getLbvServers().add(vserver);
      }
      if (logger.isDebugEnabled())
        logger.debug(
            logPrefix
                + "GSLB Server "
                + vServerName
                + " saved LB virtual server VIPs "
                + vServerExt.getLbvServers());
      // create a map of the lbvservers and the gslb that it is bound to on this target
      // we use this to determine if we need to create vApps for
      // lbvservers when we encounter them on this target
      for (String lbvServer : lbvServers) {
        lbvServerVIPToGslbVAppMap.put(lbvServer, gslb);
      }

      // set properties in the virtual app and the discovery extension
      // and create the transaction commodity sold
      gslb =
          createGslbVirtualApplication(
              gslb,
              target,
              vServerExt,
              vServerName, // service name as the local name);
              vServerUuid, // uuid for the gslb vapp containing its service name only
              vServerType, // service type
              NetScalerUtil.GSLB_VSERVER, // class name for the poller
              lbvServers // list of lbvservers configured in the gslb on this target
              );
    }
    return gslb;
  }
  /**
   * parse Server - Parse load balancing virtual server configuration object obtained from the
   * NetScaler appliance and create a VirtualApplication and discovery extension.
   *
   * @param vServer Configuration for Load Balancing Virtual Server resource from the NetScaler
   *     appliance
   * @return VirtualApplication object
   */
  private VirtualApplication parseServer(lbvserver vServer) {

    if (logger.isTraceEnabled()) logger.trace(logPrefix + vServer);

    // Retrieve load balancing virtual server information
    String vServerName, vServerUuid, vServerIP, vServerType;
    int vServerPort;
    try {
      vServerName =
          vServer.get_name(); // name of the loading balancing virtual server, is changeable
      vServerIP = vServer.get_ipv46(); // IPv4 or IPv6 address assigned to the virtual server
      vServerPort = vServer.get_port(); // Port number for the virtual server
      vServerUuid = NetScalerUtil.getServerUuid(vServerIP, vServerPort, target); // create uuid
      vServerType =
          vServer.get_servicetype(); // Protocol used by the service (also called the service type)
      lbvserver_ip2serviceName.put(vServerIP, vServerName);
      if (logger.isDebugEnabled())
        logger.debug(
            logPrefix
                + "Load Balancing Server IP:: "
                + vServerIP
                + "::port "
                + vServerPort
                + "::service name "
                + vServerName
                + "::service type "
                + vServerType);
    } catch (Exception e) {
      exceptions = true;
      if (logger.isDebugEnabled())
        logger.error(logPrefix + "Failed retrieving virtual server information ", e);
      else logger.error(logPrefix + "Failed retrieving virtual server information " + e);
      NotificationsManagerImpl.vmtMANAGER.createException(
          target, e, NotificationCategory.DISCOVERY, VMTSeverity.MAJOR);
      return null;
    }

    // Get the binding objects for the services bound to each load balancer virtual server on the
    // appliance
    lbvserver_service_binding[] bindObjs =
        NetScalerUtil.getBoundServices(
            session, vServerName,
            target, logPrefix);
    if (logger.isTraceEnabled()) {
      int length = (bindObjs == null) ? 0 : bindObjs.length;
      logger.trace(
          logPrefix
              + "Load Balancing Server "
              + vServerName
              + " has "
              + length
              + " bound services");
    }

    // get application services information
    ip2port2serviceName.clear();
    ip2NameIPPort.clear();
    if (bindObjs != null) {
      for (lbvserver_service_binding bind : bindObjs) {
        String serviceIP, serviceName;
        Integer servicePort;
        try {
          serviceIP = bind.get_ipv46();
          serviceName = bind.get_servicename();
          servicePort = bind.get_port();
          // this info is needed to find and attach the applications objects
          // from the topology during post processing
          Map<Integer, String> port2name = new HashMap<Integer, String>();
          ip2port2serviceName.put(serviceIP, port2name);
          port2name.put(servicePort, serviceName);
          // we need this info (app service name::IP::Port) for saving in the gslb vApp
          ip2NameIPPort.put(serviceIP, serviceName + "::" + serviceIP + "::" + servicePort);
        } catch (Exception e) {
          exceptions = true;
          if (logger.isDebugEnabled())
            logger.error(
                logPrefix
                    + "Failed retrieving services information for virtual server with name "
                    + vServerName
                    + " ",
                e);
          else
            logger.error(
                logPrefix
                    + "Failed retrieving services information for virtual server with name "
                    + vServerName
                    + " "
                    + e);
          NotificationsManagerImpl.vmtMANAGER.createException(
              target, e, NotificationCategory.DISCOVERY, VMTSeverity.MINOR);
          continue;
        }
      }
    }
    if (logger.isDebugEnabled()) {
      logger.debug(
          logPrefix + "on lbvserver: " + vServerName + " app services ---> " + ip2NameIPPort);
      logger.debug(
          logPrefix + "on lbvserver: " + vServerName + " app services ---> " + ip2port2serviceName);
    }
    // whether to create vApp for this lbvserver VIP or not depends if it is managed by the gslb,
    // check if this VIP is in the service bindings for the gslb service configured on this target
    boolean createVApp = true;
    if (lbvServerVIPToGslbVAppMap.containsKey(vServerIP)) {
      // found this Virtual IP on this target
      // don't create the vApp for this lbvserver,
      createVApp = false;
      // Save all the application services info in the associated gslb vApp
      updateGslbVAppAgent(vServerIP, vServerPort);
    }

    // ------------ create objects ----------
    if (createVApp) {
      // Create the VirtualApplication object corresponding to the load balancing virtual server
      // discovered in NetScaler
      VirtualApplication lb =
          (VirtualApplication)
              createObject(
                  AbstractionPackage.eINSTANCE.getVirtualApplication(),
                  "VApp-" + vServerName,
                  vServerUuid);
      // create VirtualApp Discovery extension
      NetScalerVirtualAppDiscExt vServerExt =
          (NetScalerVirtualAppDiscExt)
              lb.createExtension(
                  DiscoveryExtensionsPackage.eINSTANCE.getNetScalerVirtualAppDiscExt());
      // set the properties for the vApp and discovery extension and also save the app services info
      // in the discovery extension
      return createVirtualApplication(
          lb,
          target,
          vServerExt,
          vServerName,
          vServerIP,
          vServerPort, // lbverserver's service name, ip and port
          vServerUuid,
          vServerType,
          ip2port2serviceName, // mappings of the connected services
          NetScalerUtil.LB_VSERVER // "lbvserver"
          );
    }

    return null;
  } // end parse server