content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package com.cloud.network.resource;
  
  import java.io.IOException;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.security.KeyManagementException;
  import java.security.KeyStoreException;
  import java.security.NoSuchAlgorithmException;
  import java.security.UnrecoverableKeyException;
  import java.security.cert.CertificateException;
  import java.security.cert.X509Certificate;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  
  import javax.naming.ConfigurationException;
  
  import org.apache.commons.lang.StringEscapeUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.http.HttpResponse;
  import org.apache.http.HttpStatus;
  import org.apache.http.client.ClientProtocolException;
  import org.apache.http.client.HttpClient;
  import org.apache.http.client.methods.HttpGet;
  import org.apache.http.client.methods.HttpPost;
  import org.apache.http.conn.ClientConnectionManager;
  import org.apache.http.conn.scheme.Scheme;
  import org.apache.http.conn.scheme.SchemeRegistry;
  import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
  import org.apache.http.conn.ssl.SSLSocketFactory;
  import org.apache.http.conn.ssl.TrustStrategy;
  import org.apache.http.entity.StringEntity;
  import org.apache.http.impl.client.DefaultHttpClient;
  import org.apache.http.impl.conn.BasicClientConnectionManager;
  import org.apache.http.util.EntityUtils;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import com.google.gson.Gson;
  
  import com.cloud.agent.IAgentControl;
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.Command;
  import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer;
  import com.cloud.agent.api.ExternalNetworkResourceUsageCommand;
  import com.cloud.agent.api.MaintainAnswer;
  import com.cloud.agent.api.MaintainCommand;
  import com.cloud.agent.api.NetScalerImplementNetworkCommand;
  import com.cloud.agent.api.PingCommand;
  import com.cloud.agent.api.ReadyAnswer;
  import com.cloud.agent.api.ReadyCommand;
  import com.cloud.agent.api.StartupCommand;
  import com.cloud.agent.api.StartupExternalLoadBalancerCommand;
  import com.cloud.agent.api.UnsupportedAnswer;
  import com.cloud.agent.api.routing.DestroyLoadBalancerApplianceCommand;
  import com.cloud.agent.api.routing.GlobalLoadBalancerConfigCommand;
  import com.cloud.agent.api.routing.HealthCheckLBConfigAnswer;
  import com.cloud.agent.api.routing.HealthCheckLBConfigCommand;
  import com.cloud.agent.api.routing.IpAssocAnswer;
  import com.cloud.agent.api.routing.IpAssocCommand;
  import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
  import com.cloud.agent.api.routing.SetStaticNatRulesCommand;
  import com.cloud.agent.api.to.IpAddressTO;
  import com.cloud.agent.api.to.LoadBalancerTO;
  import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO;
  import com.cloud.agent.api.to.LoadBalancerTO.StickinessPolicyTO;
  import com.cloud.host.Host;
  import com.cloud.host.Host.Type;
  import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType;
  import com.cloud.resource.ServerResource;
  import com.cloud.serializer.GsonHelper;
  import com.cloud.utils.NumbersUtil;
  import com.cloud.utils.exception.ExecutionException;
  import com.cloud.utils.net.NetUtils;
  
  class NccHttpCode {
      static final String INTERNAL_ERROR  = "INTERNAL ERROR";
      static final String NOT_FOUND = "NOT FOUND";
      static final String JOB_ID = "Job_id";
      static final String UNAUTHORIZED  = "UNAUTHORIZED";
  }
  
  public class NetScalerControlCenterResource implements ServerResource {
  
      // deployment configuration
      private String _name;
      private String _zoneId;
      private String _ip;
      private String _username;
      private String _password;
      private String _publicInterface;
      private String _privateInterface;
      private Integer _numRetries;
      private Long _nccCmdTimeout;
      private String _guid;
      private boolean _inline;
      private String _deviceName;
      private String _sessionid;
      public static final int DEFAULT_PORT = 443;
      private static final Gson s_gson = GsonHelper.getGson();
      protected Logger logger = LogManager.getLogger(NetScalerControlCenterResource.class);
      protected Gson _gson;
      private final String _objectNamePathSep = "-";
      final String protocol="https";
      private static String nccsession;
      private int pingCount = 0;
  
      public NetScalerControlCenterResource() {
          _gson = GsonHelper.getGsonLogger();
      }
  
  
      public static String get_nccsession() {
          return nccsession;
      }
  
  
      public static void set_nccsession(String nccsession) {
          NetScalerControlCenterResource.nccsession = nccsession;
      }
  
  
      @Override
      public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
          JSONObject jsonResponse = null;
          try {
              _name = (String)params.get("name");
              if (_name == null) {
                  throw new ConfigurationException("Unable to find name in the configuration parameters");
              }
  
              _zoneId = (String)params.get("zoneId");
              if (_zoneId == null) {
                  throw new ConfigurationException("Unable to find zone Id  in the configuration parameters");
              }
  
              _ip = (String)params.get("ip");
              if (_ip == null) {
                  throw new ConfigurationException("Unable to find IP address in the configuration parameters");
              }
  
              _username = (String)params.get("username");
              if (_username == null) {
                  throw new ConfigurationException("Unable to find username in the configuration parameters");
              }
  
              _password =  (String)params.get("password");
              if (_password == null) {
                  throw new ConfigurationException("Unable to find password in the configuration parameters");
              }
  
              _numRetries = NumbersUtil.parseInt((String)params.get("numretries"), 2);
  
              _guid = (String)params.get("guid");
              if (_guid == null) {
                  throw new ConfigurationException("Unable to find the guid in the configuration parameters");
              }
  
              _deviceName = (String)params.get("deviceName");
              if (_deviceName == null) {
                  throw new ConfigurationException("Unable to find the device name in the configuration parameters");
              }
              _nccCmdTimeout = NumbersUtil.parseLong((String)params.get("numretries"), 600000);
  
              // validate device configuration parameters
              login();
  
              return true;
          } catch (ConfigurationException e) {
              throw new ConfigurationException(e.getMessage());
          } catch (ExecutionException e) {
              logger.debug("Execution Exception :" +  e.getMessage());
              throw new ConfigurationException("Failed to add the device. Please check the device is NCC and It is reachable from Management Server.");
          }
      }
  
      public void getServicePackages() throws ExecutionException {
              String result = null;
              try {
                  URI agentUri = null;
                  agentUri =
                          new URI("https", null, _ip, DEFAULT_PORT,
                                  "/admin/v1/servicepackages", null, null);
  
                  org.json.JSONObject jsonBody = new JSONObject();
                  org.json.JSONObject jsonCredentials = new JSONObject();
                  result = getHttpRequest(jsonBody.toString(), agentUri, _sessionid);
                  logger.debug("List of Service Packages in NCC:: " + result);
                  } catch (URISyntaxException e) {
                      String errMsg = "Could not generate URI for Hyper-V agent";
                      logger.error(errMsg, e);
  
                  } catch (Exception e) {
                  throw new ExecutionException("Failed to log in to NCC device at " + _ip + " due to " + e.getMessage());
              }
      }
  
      private synchronized String login() throws ExecutionException{
          String result = null;
          JSONObject jsonResponse = null;
          try {
              URI agentUri = null;
              agentUri =
                      new URI("https", null, _ip, DEFAULT_PORT,
                              "/nitro/v2/config/" + "login", null, null);
              org.json.JSONObject jsonBody = new JSONObject();
              org.json.JSONObject jsonCredentials = new JSONObject();
              jsonCredentials.put("username", _username);
              jsonCredentials.put("password", _password);
              jsonBody.put("login", jsonCredentials);
  
              result = postHttpRequest(jsonBody.toString(), agentUri, _sessionid);
              if (result == null) {
                  throw new ConfigurationException("No Response Received from the NetScalerControlCenter Device");
              } else {
                  jsonResponse = new JSONObject(result);
                  org.json.JSONArray loginResponse = jsonResponse.getJSONArray("login");
                  _sessionid = jsonResponse.getJSONArray("login").getJSONObject(0).getString("sessionid");
                  logger.debug("New Session id from NCC :" + _sessionid);
                  set_nccsession(_sessionid);
                  logger.debug("session on Static Session variable" + get_nccsession());
              }
              logger.debug("Login to NCC Device response :: " + result);
              return result;
              } catch (URISyntaxException e) {
                  String errMsg = "Could not generate URI for Hyper-V agent";
                  logger.error(errMsg, e);
  
              } catch (JSONException e) {
                  logger.debug("JSON Exception :" +  e.getMessage());
                  throw new ExecutionException("Failed to log in to NCC device at " + _ip + " due to " + e.getMessage());
              } catch (Exception e) {
              throw new ExecutionException("Failed to log in to NCC device at " + _ip + " due to " + e.getMessage());
          }
          return result;
      }
  
      @Override
      public StartupCommand[] initialize() {
          StartupExternalLoadBalancerCommand cmd = new StartupExternalLoadBalancerCommand(Host.Type.NetScalerControlCenter);
          cmd.setName(_name);
          cmd.setDataCenter(_zoneId);
          cmd.setPod("");
          cmd.setPrivateIpAddress(_ip);
          cmd.setStorageIpAddress("");
          cmd.setVersion(NetScalerControlCenterResource.class.getPackage().getImplementationVersion());
          cmd.setGuid(_guid);
          return new StartupCommand[] {cmd};
      }
  
      @Override
      public Answer executeRequest(Command cmd) {
          return executeRequest(cmd, _numRetries);
      }
  
      private Answer executeRequest(Command cmd, int numRetries) {
          if (cmd instanceof ReadyCommand) {
              return execute((ReadyCommand)cmd);
          } else if (cmd instanceof MaintainCommand) {
              return execute((MaintainCommand)cmd);
          } else if (cmd instanceof IpAssocCommand) {
              return execute((IpAssocCommand)cmd, numRetries);
          } else if (cmd instanceof LoadBalancerConfigCommand) {
              return execute((LoadBalancerConfigCommand)cmd, numRetries);
          } else if (cmd instanceof ExternalNetworkResourceUsageCommand) {
              return execute((ExternalNetworkResourceUsageCommand)cmd, numRetries);
          } else if (cmd instanceof DestroyLoadBalancerApplianceCommand) {
              return Answer.createUnsupportedCommandAnswer(cmd);
          } else if (cmd instanceof SetStaticNatRulesCommand) {
              return execute((SetStaticNatRulesCommand)cmd, numRetries);
          } else if (cmd instanceof GlobalLoadBalancerConfigCommand) {
              return Answer.createUnsupportedCommandAnswer(cmd);
          } else if (cmd instanceof HealthCheckLBConfigCommand) {
              return execute((HealthCheckLBConfigCommand)cmd, numRetries);
          } else if (cmd instanceof NetScalerImplementNetworkCommand ) {
              return execute((NetScalerImplementNetworkCommand)cmd, numRetries);
          }
          else {
              return Answer.createUnsupportedCommandAnswer(cmd);
          }
      }
  
      private Answer execute(ReadyCommand cmd) {
          return new ReadyAnswer(cmd);
      }
  
      protected Answer execute(MaintainCommand cmd) {
          return new MaintainAnswer(cmd);
      }
  
      private void keepSessionAlive() throws ExecutionException {
          URI agentUri = null;
          try {
              agentUri =
                      new URI("https", null, _ip, DEFAULT_PORT,
                              "/cs/cca/v1/cloudstacks", null, null);
              org.json.JSONObject jsonBody = new JSONObject();
              getHttpRequest(jsonBody.toString(), agentUri, _sessionid);
              logger.debug("Keeping Session Alive");
          } catch (URISyntaxException e) {
              e.printStackTrace();
          }
      }
  
      private String queryAsyncJob(String jobId) throws ExecutionException {
          String result = null;
          try {
              URI agentUri = null;
              agentUri =
                      new URI("https", null, _ip, DEFAULT_PORT,
                              "/admin/v1/journalcontexts/" + jobId, null, null);
  
              org.json.JSONObject jsonBody = new JSONObject();
  
              long startTick = System.currentTimeMillis();
              while (System.currentTimeMillis() - startTick <  _nccCmdTimeout) {
                  result = getHttpRequest(jsonBody.toString(), agentUri, _sessionid);
                  JSONObject response = new JSONObject(result);
                  if(response != null ) {
                      logger.debug("Job Status result for ["+jobId + "]:: " + result + " Tick and currentTime :" +  System.currentTimeMillis() +" -" + startTick + "job cmd timeout :" +_nccCmdTimeout);
                      String status = response.getJSONObject("journalcontext").getString("status").toUpperCase();
                      String message = response.getJSONObject("journalcontext").getString("message");
                      logger.debug("Job Status Progress Status ["+ jobId + "]:: " + status);
                      switch(status) {
                      case "FINISHED":
                              return status;
                      case "IN PROGRESS":
                          break;
                      case "ERROR, ROLLBACK IN PROGRESS":
                          break;
                      case "ERROR, ROLLBACK COMPLETED":
                          throw new ExecutionException("ERROR, ROLLBACK COMPLETED " + message);
                      case "ERROR, ROLLBACK FAILED":
                          throw new ExecutionException("ERROR, ROLLBACK FAILED " + message);
                      }
                  }
              }
  
          } catch (URISyntaxException e) {
              String errMsg = "Could not generate URI for NetScaler ControlCenter";
              logger.error(errMsg, e);
            } catch (JSONException e) {
              e.printStackTrace();
          }
          return result;
      }
      private synchronized Answer execute(NetScalerImplementNetworkCommand cmd, int numRetries) {
          String result = null;
          try {
              URI agentUri = null;
              agentUri =
                      new URI("https", null, _ip, DEFAULT_PORT,
                              "/cs/adcaas/v1/networks", null, null);
              org.json.JSONObject jsonBody = new JSONObject(cmd.getDetails());
              logger.debug("Sending Network Implement to NCC:: " + jsonBody);
              result = postHttpRequest(jsonBody.toString(), agentUri, _sessionid);
              logger.debug("Result of Network Implement to NCC:: " + result);
              result = queryAsyncJob(result);
              logger.debug("Done query async of network implement request :: " + result);
              return new Answer(cmd, true, "Successfully allocated device");
              } catch (URISyntaxException e) {
                  String errMsg = "Could not generate URI for NetScaler ControlCenter ";
                  logger.error(errMsg, e);
              } catch (ExecutionException e) {
                  if(e.getMessage().equalsIgnoreCase(NccHttpCode.NOT_FOUND)) {
                      return new Answer(cmd, true, "Successfully unallocated the device");
                  }else if(e.getMessage().startsWith("ERROR, ROLLBACK") ) {
                      logger.error(e.getMessage());
                      return new Answer(cmd, false, e.getMessage());
                  }
                  else {
                      if (shouldRetry(numRetries)) {
                          logger.debug("Retrying the command NetScalerImplementNetworkCommand retry count: " + numRetries, e);
                          return retry(cmd, numRetries);
                      } else {
                          return new Answer(cmd, false, e.getMessage());
                      }
                  }
              } catch (Exception e) {
                  if (shouldRetry(numRetries)) {
                      logger.debug("Retrying the command NetScalerImplementNetworkCommand retry count: " + numRetries, e);
                      return retry(cmd, numRetries);
                  } else {
                      return new Answer(cmd, false, e.getMessage());
                  }
              }
  
          return Answer.createUnsupportedCommandAnswer(cmd);
      }
  
      private synchronized Answer execute(IpAssocCommand cmd,  int numRetries) {
  
          String[] results = new String[cmd.getIpAddresses().length];
          int i = 0;
          IpAddressTO[] ips = cmd.getIpAddresses();
          for (IpAddressTO ip : ips) {
              results[i++] = ip.getPublicIp() + " - success";
          }
  
          return new IpAssocAnswer(cmd, results);
      }
  
      private Answer execute(HealthCheckLBConfigCommand cmd, int numRetries) {
  
          List<LoadBalancerTO> hcLB = new ArrayList<LoadBalancerTO>();
          try {
  
              LoadBalancerTO[] loadBalancers = cmd.getLoadBalancers();
  
              if (loadBalancers == null) {
                  return new HealthCheckLBConfigAnswer(hcLB);
              }
              String result = getLBHealthChecks(cmd.getNetworkId());
              JSONObject res =  new JSONObject(result);
              JSONArray lbstatus = res.getJSONArray("lbhealthstatus");
              for(int i=0; i<lbstatus.length(); i++) {
                  JSONObject lbstat = lbstatus.getJSONObject(i);
                  LoadBalancerTO loadBalancer = null;
                  JSONArray dest = lbstat.getJSONArray("destinations");
                  List<DestinationTO> listDestTo = new ArrayList<DestinationTO>();
                  for(int d=0; d<dest.length(); d++ ) {
                      JSONObject dt = dest.getJSONObject(d);
                      if( dt!=null ) {
                          DestinationTO destTO = new DestinationTO(dt.getString("destIp"), dt.getInt("destPort"), dt.getString("monitorState"));
                          listDestTo.add(destTO);
                      }
                  }
                  loadBalancer = new LoadBalancerTO(lbstat.getString("lb_uuid"),listDestTo);
                  hcLB.add(loadBalancer);
              }
          } catch (ExecutionException e) {
              logger.error("Failed to execute HealthCheckLBConfigCommand due to ", e);
              if (shouldRetry(numRetries)) {
                  return retry(cmd, numRetries);
              } else {
                  return new HealthCheckLBConfigAnswer(hcLB);
              }
          } catch (Exception e) {
              logger.error("Failed to execute HealthCheckLBConfigCommand due to ", e);
              if (shouldRetry(numRetries)) {
                  return retry(cmd, numRetries);
              } else {
                  return new HealthCheckLBConfigAnswer(hcLB);
              }
          }
          return new HealthCheckLBConfigAnswer(hcLB);
      }
  
      private String getLBHealthChecks(long networkid) throws ExecutionException  {
          URI agentUri = null;
          String response = null;
          try {
              agentUri =
                      new URI("https", null, _ip, DEFAULT_PORT,
                              "/cs/adcaas/v1/networks/"+ networkid +"/lbhealthstatus", null, null);
              org.json.JSONObject jsonBody = new JSONObject();
              response = getHttpRequest(jsonBody.toString(), agentUri, _sessionid);
              logger.debug("LBHealthcheck Response :" + response);
          } catch (URISyntaxException e) {
              e.printStackTrace();
          }
          return response;
      }
      private synchronized Answer execute(LoadBalancerConfigCommand cmd, int numRetries) {
          try {
              LoadBalancerTO[] loadBalancers = cmd.getLoadBalancers();
              if (loadBalancers == null) {
                  return new Answer(cmd);
              }
              JSONObject lbConfigPaylod = new JSONObject(cmd);
              String gsonLBConfig =  _gson.toJson(cmd);
              URI agentUri = null;
              agentUri =
                      new URI("https", null, _ip, DEFAULT_PORT,
                              "/cs/adcaas/v1/loadbalancerCmds", null, null);
              JSONObject lbConfigCmd = new JSONObject();
              JSONObject lbcmd = new JSONObject(gsonLBConfig);
              logger.debug("LB config from gsonstring to JSONObject : " +  lbcmd.toString() + "\n" + "gson cmd is :: \t" + gsonLBConfig);
              lbConfigCmd.put("LoadBalancerConfigCommand",  lbcmd.getJSONArray("loadBalancers"));
              logger.debug("LB config paylod : " +  lbConfigCmd.toString());
  
              String result = postHttpRequest(lbConfigCmd.toString(), agentUri, _sessionid);
              logger.debug("Result of lbconfigcmg is "+ result);
              result = queryAsyncJob(result);
              logger.debug("Done query async of LB ConfigCmd implement request and result:: " + result);
              return new Answer(cmd);
          } catch (ExecutionException e) {
              logger.error("Failed to execute LoadBalancerConfigCommand due to ", e);
              if(e.getMessage().equalsIgnoreCase(NccHttpCode.NOT_FOUND)) {
                  return new Answer(cmd, true, "LB Rule is not present in NS device. So returning as removed the LB Rule");
              } else  if(e.getMessage().startsWith("ERROR, ROLLBACK COMPLETED") || e.getMessage().startsWith("ERROR, ROLLBACK FAILED")) {
                  logger.error("Failed to execute LoadBalancerConfigCommand due to : " + e.getMessage());
                  return new Answer(cmd, false, e.getMessage());
              } else if (e.getMessage().startsWith(NccHttpCode.INTERNAL_ERROR)) {
                  logger.error("Failed to execute LoadBalancerConfigCommand as Internal Error returning Internal error ::" + e.getMessage() );
                  return new Answer(cmd, false, e.getMessage());
              }
              if (shouldRetry(numRetries)) {
                  return retry(cmd, numRetries);
              } else {
                  return new Answer(cmd, false, e.getMessage());
              }
          } catch (Exception e) {
              logger.error("Failed to execute LoadBalancerConfigCommand due to ", e);
              if (shouldRetry(numRetries)) {
                  return retry(cmd, numRetries);
              } else {
                  return new Answer(cmd, false, e.getMessage());
              }
          }
      }
  
      private synchronized Answer execute(SetStaticNatRulesCommand cmd, int numRetries) {
          return Answer.createUnsupportedCommandAnswer(cmd);
      }
  
      private synchronized Answer execute(ExternalNetworkResourceUsageCommand cmd, int numRetries) {
          try {
  
              return getPublicIpBytesSentAndReceived(cmd);
  
          } catch (ExecutionException e) {
              if (shouldRetry(numRetries)) {
                  return retry(cmd, numRetries);
              } else {
                  return new ExternalNetworkResourceUsageAnswer(cmd, e);
              }
          }
      }
  
  
      private String getNetScalerProtocol(LoadBalancerTO loadBalancer) throws ExecutionException {
          String port = Integer.toString(loadBalancer.getSrcPort());
          String lbProtocol = loadBalancer.getLbProtocol();
          StickinessPolicyTO[] stickyPolicies = loadBalancer.getStickinessPolicies();
          String nsProtocol = "TCP";
  
          if (lbProtocol == null)
              lbProtocol = loadBalancer.getProtocol();
  
          if ((stickyPolicies != null) && (stickyPolicies.length > 0) && (stickyPolicies[0] != null)) {
              StickinessPolicyTO stickinessPolicy = stickyPolicies[0];
              if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) ||
                      (StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()))) {
                  nsProtocol = "HTTP";
                  return nsProtocol;
              }
          }
  
          if (lbProtocol.equalsIgnoreCase(NetUtils.SSL_PROTO) || lbProtocol.equalsIgnoreCase(NetUtils.HTTP_PROTO))
              return lbProtocol.toUpperCase();
  
          if (port.equals(NetUtils.HTTP_PORT)) {
              nsProtocol = "HTTP";
          } else if (NetUtils.TCP_PROTO.equalsIgnoreCase(lbProtocol)) {
              nsProtocol = "TCP";
          } else if (NetUtils.UDP_PROTO.equalsIgnoreCase(lbProtocol)) {
              nsProtocol = "UDP";
          }
  
          return nsProtocol;
      }
  
  
      private ExternalNetworkResourceUsageAnswer getPublicIpBytesSentAndReceived(ExternalNetworkResourceUsageCommand cmd) throws ExecutionException {
          ExternalNetworkResourceUsageAnswer answer = new ExternalNetworkResourceUsageAnswer(cmd);
          long networkid = cmd.getNetworkid();
          try {
              //TODO send GET cmd to get the network stats
  
              URI agentUri = null;
              String response = null;
              try {
                  agentUri =
                          new URI("https", null, _ip, DEFAULT_PORT,
                                  "/cs/adcaas/v1/networks/"+ networkid +"/ipStats", null, null);
                  org.json.JSONObject jsonBody = new JSONObject();
                  response = getHttpRequest(jsonBody.toString(), agentUri, _sessionid);
                  JSONArray statsIPList = null;
                  if(response !=null ) {
                      statsIPList = new JSONObject(response).getJSONObject("stats") .getJSONArray("ipBytes");
                  }
                  if(statsIPList != null) {
                      for(int i=0; i<statsIPList.length(); i++) {
                          JSONObject ipstat = statsIPList.getJSONObject(i);
                          JSONObject ipvalues =  ipstat.getJSONObject("ipstats");
                          if(ipstat != null) {
                              long[] bytesSentAndReceived = new long[] {0, 0};
                              bytesSentAndReceived[0] = ipvalues.getLong("received");
                              bytesSentAndReceived[1] = ipvalues.getLong("sent");
  
                              if (bytesSentAndReceived[0] >= 0 && bytesSentAndReceived[1] >= 0) {
                                  answer.ipBytes.put(ipstat.getString("ip"), bytesSentAndReceived);
                              }
                         }
                      }
                  }
                  logger.debug("IPStats Response :" + response);
              } catch (URISyntaxException e) {
                  e.printStackTrace();
              } catch (ExecutionException e) {
                  logger.debug("Seesion Alive" + e.getMessage());
                  e.printStackTrace();
              }
  
          } catch (Exception e) {
              logger.error("Failed to get bytes sent and received statistics due to " + e);
              throw new ExecutionException(e.getMessage());
          }
  
          return answer;
      }
  
      private Answer retry(Command cmd, int numRetries) {
          int numRetriesRemaining = numRetries - 1;
          logger.warn("Retrying " + cmd.getClass().getSimpleName() + ". Number of retries remaining: " + numRetriesRemaining);
          return executeRequest(cmd, numRetriesRemaining);
      }
  
      private boolean shouldRetry(int numRetries) {
          try {
              if (numRetries > 0) {
                  login();
                  return true;
              }
          } catch (Exception e) {
              logger.error("Failed to log in to Netscaler ControlCenter device at " + _ip + " due to " + e.getMessage());
              return false;
          }
          return false;
      }
  
  
      @Override
      public IAgentControl getAgentControl() {
          return null;
      }
  
      private boolean refreshNCCConnection() {
          boolean ret = false;
          try {
              keepSessionAlive();
              return true;
          } catch (ExecutionException ex) {
              logger.debug("Failed to keep up the session alive ", ex);
          }
          return ret;
      }
  
      @Override
      public PingCommand getCurrentStatus(long id) {
          pingCount++;
          if (pingCount > 10 && refreshNCCConnection()) {
              pingCount = 0;
          }
          return new PingCommand(Host.Type.NetScalerControlCenter, id);
      }
  
      @Override
      public Type getType() {
          return Host.Type.NetScalerControlCenter ;
      }
  
      @Override
      public void setAgentControl(IAgentControl agentControl) {
          return;
      }
  
      @Override
      public String getName() {
          return _name;
      }
  
      @Override
      public void setName(String name) {
      }
  
      @Override
      public boolean start() {
          return true;
      }
  
      @Override
      public boolean stop() {
          return true;
      }
  
      @Override
      public void disconnected() {
          return;
      }
  
      @Override
      public void setConfigParams(Map<String, Object> params) {
      }
  
      @Override
      public Map<String, Object> getConfigParams() {
          return null;
      }
  
      @Override
      public int getRunLevel() {
          return 0;
      }
  
      @Override
      public void setRunLevel(int level) {
      }
  
      public String getSessionID() {
          return _sessionid;
      }
      public static String cleanPassword(String logString) {
          String cleanLogString = null;
          if (logString != null) {
              cleanLogString = logString;
              String[] temp = logString.split(",");
              int i = 0;
              if (temp != null) {
                  while (i < temp.length) {
                      temp[i] = com.cloud.utils.StringUtils.cleanString(temp[i]);
                      i++;
                  }
                  List<String> stringList = new ArrayList<String>();
                  Collections.addAll(stringList, temp);
                  cleanLogString = StringUtils.join(stringList, ",");
              }
          }
          return cleanLogString;
      }
      public HttpClient getHttpClient() {
  
          HttpClient httpClient = null;
          TrustStrategy easyStrategy = new TrustStrategy() {
              @Override
              public boolean isTrusted(X509Certificate[] chain, String authType)
                      throws CertificateException {
                  return true;
              }
          };
  
          try {
              SSLSocketFactory sf = new SSLSocketFactory(easyStrategy, new AllowAllHostnameVerifier());
              SchemeRegistry registry = new SchemeRegistry();
              registry.register(new Scheme("https", DEFAULT_PORT, sf));
              ClientConnectionManager ccm = new BasicClientConnectionManager(registry);
              httpClient = new DefaultHttpClient(ccm);
          } catch (KeyManagementException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          } catch (UnrecoverableKeyException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          } catch (NoSuchAlgorithmException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          } catch (KeyStoreException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          }
          return httpClient;
      }
  
      public String getHttpRequest(final String jsonCmd, final URI agentUri, String sessionID) throws ExecutionException {
          // Using Apache's HttpClient for HTTP POST
          // Java-only approach discussed at on StackOverflow concludes with
          // comment to use Apache HttpClient
          // http://stackoverflow.com/a/2793153/939250, but final comment is to
          // use Apache.
          String logMessage = StringEscapeUtils.unescapeJava(jsonCmd);
          logMessage = cleanPassword(logMessage);
          logger.debug("GET request to " + agentUri.toString()
                  + " with contents " + logMessage);
  
          // Create request
          HttpClient httpClient = getHttpClient();
          String result = null;
  
          // TODO: are there timeout settings and worker thread settings to tweak?
          try {
              HttpGet request = new HttpGet(agentUri);
  
              // JSON encode command
              // Assumes command sits comfortably in a string, i.e. not used for
              // large data transfers
              StringEntity cmdJson = new StringEntity(jsonCmd);
              request.addHeader("content-type", "application/json");
              request.addHeader("Cookie", "SessId=" + sessionID);
              logger.debug("Sending cmd to " + agentUri.toString()
                      + " cmd data:" + logMessage);
              HttpResponse response = httpClient.execute(request);
  
              // Unsupported commands will not route.
              if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                  String errMsg = "Failed to send : HTTP error code : " + response.getStatusLine().getStatusCode();
                  logger.error(errMsg);
                  String unsupportMsg = "Unsupported command " + agentUri.getPath() + ".  Are you sure you got the right f of" + " server?";
                  Answer ans = new UnsupportedAnswer(null, unsupportMsg);
                  logger.error(ans);
                  result = s_gson.toJson(new Answer[] {ans});
              } else if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                  String errMsg = "Failed send to " + agentUri.toString() + " : HTTP error code : " + response.getStatusLine().getStatusCode();
                  logger.error(errMsg);
                  throw new ExecutionException("UNAUTHORIZED");
              } else {
                  result = EntityUtils.toString(response.getEntity());
                  String logResult = cleanPassword(StringEscapeUtils.unescapeJava(result));
                  logger.debug("Get response is " + logResult);
              }
          } catch (ClientProtocolException protocolEx) {
              // Problem with HTTP message exchange
              logger.error(protocolEx);
          } catch (IOException connEx) {
              // Problem with underlying communications
              logger.error(connEx);
          } finally {
              httpClient.getConnectionManager().shutdown();
          }
          return result;
      }
  
      public String postHttpRequest(final String jsonCmd, final URI agentUri, String sessionID) throws ExecutionException {
          // Using Apache's HttpClient for HTTP POST
          // Java-only approach discussed at on StackOverflow concludes with
          // comment to use Apache HttpClient
          // http://stackoverflow.com/a/2793153/939250, but final comment is to
          // use Apache.
          String logMessage = StringEscapeUtils.unescapeJava(jsonCmd);
          logMessage = cleanPassword(logMessage);
          logger.debug("POST request to " + agentUri.toString()
                  + " with contents " + logMessage);
  
          // Create request
          HttpClient httpClient = getHttpClient();
          TrustStrategy easyStrategy = new TrustStrategy() {
              @Override
              public boolean isTrusted(X509Certificate[] chain, String authType)
                      throws CertificateException {
                  return true;
              }
          };
  
          try {
              SSLSocketFactory sf = new SSLSocketFactory(easyStrategy, new AllowAllHostnameVerifier());
              SchemeRegistry registry = new SchemeRegistry();
              registry.register(new Scheme("https", DEFAULT_PORT, sf));
              ClientConnectionManager ccm = new BasicClientConnectionManager(registry);
              httpClient = new DefaultHttpClient(ccm);
          } catch (KeyManagementException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          } catch (UnrecoverableKeyException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          } catch (NoSuchAlgorithmException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          } catch (KeyStoreException e) {
              logger.error("failed to initialize http client " + e.getMessage());
          }
  
          String result = null;
  
          // TODO: are there timeout settings and worker thread settings to tweak?
          try {
              HttpPost request = new HttpPost(agentUri);
  
              // JSON encode command
              // Assumes command sits comfortably in a string, i.e. not used for
              // large data transfers
              StringEntity cmdJson = new StringEntity(jsonCmd);
              request.addHeader("content-type", "application/json");
              request.addHeader("Cookie", "SessId=" + sessionID);
              request.setEntity(cmdJson);
              logger.debug("Sending cmd to " + agentUri.toString()
                      + " cmd data:" + logMessage + "SEssion id: " + sessionID);
              HttpResponse response = httpClient.execute(request);
  
              // Unsupported commands will not route.
              if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                  String errMsg = "Failed : HTTP error code : " + response.getStatusLine().getStatusCode();
                  throw new ExecutionException(NccHttpCode.NOT_FOUND);
              } else if ((response.getStatusLine().getStatusCode() != HttpStatus.SC_OK ) && (response.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED )) {
                  String errMsg = "Command Not Success " + agentUri.toString() + " : HTTP error code : " + response.getStatusLine().getStatusCode();
                  logger.error(errMsg);
                  throw new ExecutionException(NccHttpCode.INTERNAL_ERROR + " " + errMsg);
              } else if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                  //Successfully created the resource in the NCC, Now get the Job ID and send to the response
                  // make login request and store new session id
                  throw new ExecutionException(NccHttpCode.UNAUTHORIZED);
              } else if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
                  //Successfully created the resource in the NCC, Now get the Job ID and send to the response
                  result = response.getFirstHeader(NccHttpCode.JOB_ID).getValue();
              } else {
                  result = EntityUtils.toString(response.getEntity());
                  String logResult = cleanPassword(StringEscapeUtils.unescapeJava(result));
                  logger.debug("POST response is " + logResult);
              }
  
          } catch (ClientProtocolException protocolEx) {
              // Problem with HTTP message exchange
              logger.error(protocolEx);
          } catch (IOException connEx) {
              // Problem with underlying communications
              logger.error(connEx);
          } finally {
              httpClient.getConnectionManager().shutdown();
          }
          return result;
      }
  }
  