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.BufferedReader;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.net.URLEncoder;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.List;
  import java.util.Map;
  import java.util.StringTokenizer;
  
  import javax.naming.ConfigurationException;
  
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import com.cloud.agent.IAgentControl;
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.Command;
  import com.cloud.agent.api.DirectNetworkUsageAnswer;
  import com.cloud.agent.api.DirectNetworkUsageCommand;
  import com.cloud.agent.api.MaintainAnswer;
  import com.cloud.agent.api.MaintainCommand;
  import com.cloud.agent.api.PingCommand;
  import com.cloud.agent.api.ReadyAnswer;
  import com.cloud.agent.api.ReadyCommand;
  import com.cloud.agent.api.RecurringNetworkUsageAnswer;
  import com.cloud.agent.api.RecurringNetworkUsageCommand;
  import com.cloud.agent.api.StartupCommand;
  import com.cloud.agent.api.StartupTrafficMonitorCommand;
  import com.cloud.host.Host;
  import com.cloud.resource.ServerResource;
  import com.cloud.utils.exception.ExecutionException;
  import java.net.HttpURLConnection;
  
  public class TrafficSentinelResource implements ServerResource {
  
      private String _name;
      private String _zoneId;
      private String _ip;
      private String _guid;
      private String _url;
      private String _inclZones;
      private String _exclZones;
  
      protected Logger logger = LogManager.getLogger(getClass());
  
      @Override
      public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
          try {
  
              _name = name;
  
              _zoneId = (String)params.get("zone");
              if (_zoneId == null) {
                  throw new ConfigurationException("Unable to find zone");
              }
  
              _ip = (String)params.get("ipaddress");
              if (_ip == null) {
                  throw new ConfigurationException("Unable to find IP");
              }
  
              _guid = (String)params.get("guid");
              if (_guid == null) {
                  throw new ConfigurationException("Unable to find the guid");
              }
  
              _url = (String)params.get("url");
              if (_url == null) {
                  throw new ConfigurationException("Unable to find url");
              }
  
              _inclZones = (String)params.get("inclZones");
              _exclZones = (String)params.get("exclZones");
  
              return true;
          } catch (Exception e) {
              throw new ConfigurationException(e.getMessage());
          }
  
      }
  
      @Override
      public StartupCommand[] initialize() {
          StartupTrafficMonitorCommand cmd = new StartupTrafficMonitorCommand();
          cmd.setName(_name);
          cmd.setDataCenter(_zoneId);
          cmd.setPod("");
          cmd.setPrivateIpAddress(_ip);
          cmd.setStorageIpAddress("");
          cmd.setVersion(TrafficSentinelResource.class.getPackage().getImplementationVersion());
          cmd.setGuid(_guid);
          return new StartupCommand[] {cmd};
      }
  
      @Override
      public Host.Type getType() {
          return Host.Type.TrafficMonitor;
      }
  
      @Override
      public String getName() {
          return _name;
      }
  
      @Override
      public PingCommand getCurrentStatus(final long id) {
          return new PingCommand(Host.Type.TrafficMonitor, id);
      }
  
      @Override
      public boolean start() {
          return true;
      }
  
      @Override
      public boolean stop() {
          return true;
      }
  
      @Override
      public void disconnected() {
          return;
      }
  
      @Override
      public IAgentControl getAgentControl() {
          return null;
      }
  
      @Override
      public void setAgentControl(IAgentControl agentControl) {
          return;
      }
  
      @Override
      public Answer executeRequest(Command cmd) {
          if (cmd instanceof ReadyCommand) {
              return execute((ReadyCommand)cmd);
          } else if (cmd instanceof MaintainCommand) {
              return execute((MaintainCommand)cmd);
          } else if (cmd instanceof DirectNetworkUsageCommand) {
              return execute((DirectNetworkUsageCommand)cmd);
          } else if (cmd instanceof RecurringNetworkUsageCommand) {
              return execute((RecurringNetworkUsageCommand)cmd);
          } else {
              return Answer.createUnsupportedCommandAnswer(cmd);
          }
      }
  
      private Answer execute(ReadyCommand cmd) {
          return new ReadyAnswer(cmd);
      }
  
      private synchronized RecurringNetworkUsageAnswer execute(RecurringNetworkUsageCommand cmd) {
          return new RecurringNetworkUsageAnswer(cmd);
      }
  
      private synchronized DirectNetworkUsageAnswer execute(DirectNetworkUsageCommand cmd) {
          try {
              return getPublicIpBytesSentAndReceived(cmd);
          } catch (ExecutionException e) {
              return new DirectNetworkUsageAnswer(cmd, e);
          }
      }
  
      private Answer execute(MaintainCommand cmd) {
          return new MaintainAnswer(cmd);
      }
  
      private DirectNetworkUsageAnswer getPublicIpBytesSentAndReceived(DirectNetworkUsageCommand cmd) throws ExecutionException {
          DirectNetworkUsageAnswer answer = new DirectNetworkUsageAnswer(cmd);
  
          try {
              //Direct Network Usage
              URL trafficSentinel;
              //Use Global include/exclude zones if there are no per TS zones
              if (_inclZones == null) {
                  _inclZones = cmd.getIncludeZones();
              }
  
              if (_exclZones == null) {
                  _exclZones = cmd.getExcludeZones();
              }
  
              BufferedReader in = null;
              OutputStream os = null;
              try {
                  //Query traffic Sentinel using POST method. 3 parts to the connection call and subsequent writing.
  
                  //Part 1 - Connect to the URL of the traffic sentinel's instance.
                  trafficSentinel = new URL(_url + "/inmsf/Query");
                  String postData = "script="+URLEncoder.encode(getScript(cmd.getPublicIps(), cmd.getStart(), cmd.getEnd()), "UTF-8")+"&authenticate=basic&resultFormat=txt";
                  HttpURLConnection con = (HttpURLConnection) trafficSentinel.openConnection();
                  con.setRequestMethod("POST");
                  con.setRequestProperty("content-length", String.valueOf(postData.length()));
                  con.setDoOutput(true);
  
                  //Part 2 - Write Data
                  os = con.getOutputStream();
                  os.write(postData.getBytes("UTF-8"));
  
                  //Part 3 - Read response of the request
                  in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                  String inputLine;
  
                  while ((inputLine = in.readLine()) != null) {
                      //Parse the script output
                      StringTokenizer st = new StringTokenizer(inputLine, ",");
                      if (st.countTokens() == 3) {
                          String publicIp = st.nextToken();
                          Long bytesSent = new Long(st.nextToken());
                          Long bytesRcvd = new Long(st.nextToken());
                          long[] bytesSentAndReceived = new long[2];
                          bytesSentAndReceived[0] = bytesSent;
                          bytesSentAndReceived[1] = bytesRcvd;
                          answer.put(publicIp, bytesSentAndReceived);
                      }
                  }
              } catch (MalformedURLException e1) {
                  logger.info("Invalid Traffic Sentinel URL", e1);
                  throw new ExecutionException(e1.getMessage());
              } catch (IOException e) {
                  logger.debug("Error in direct network usage accounting", e);
                  throw new ExecutionException(e.getMessage());
              } finally {
                  if (os != null) {
                      os.close();
                  }
                  if (in != null) {
                      in.close();
                  }
              }
          } catch (Exception e) {
              logger.debug(e);
              throw new ExecutionException(e.getMessage());
          }
          return answer;
      }
  
      private String getScript(List<String> ips, Date start, Date end) {
          String IpAddresses = "";
          for (int i = 0; i < ips.size(); i++) {
              IpAddresses += ips.get(i);
              if (i != (ips.size() - 1)) {
                  // Append comma for all Ips except the last Ip
                  IpAddresses += ",";
              }
          }
          String destZoneCondition = "";
          if (_inclZones != null && !_inclZones.isEmpty()) {
              destZoneCondition = " & destinationzone = " + _inclZones;
          }
          if (_exclZones != null && !_exclZones.isEmpty()) {
              destZoneCondition += " & destinationzone != " + _exclZones;
          }
  
          String srcZoneCondition = "";
          if (_inclZones != null && !_inclZones.isEmpty()) {
              srcZoneCondition = " & sourcezone = " + _inclZones;
          }
          if (_exclZones != null && !_exclZones.isEmpty()) {
              srcZoneCondition += " & sourcezone != " + _exclZones;
          }
  
          String startDate = getDateString(start);
          String endtDate = getDateString(end);
          StringBuffer sb = new StringBuffer();
          sb.append("var q = Query.topN(\"historytrmx\",");
          sb.append("                 \"ipsource,bytes\",");
          sb.append("                 \"ipsource = " + IpAddresses + destZoneCondition + "\",");
          sb.append("                 \"" + startDate + ", " + endtDate + "\",");
          sb.append("                 \"bytes\",");
          sb.append("                 100000);");
          sb.append("var totalsSent = {};");
          sb.append("var t = q.run(");
          sb.append("  function(row,table) {");
          sb.append("    if(row[0]) {    ");
          sb.append("      totalsSent[row[0]] = row[1];");
          sb.append("    }");
          sb.append("  });");
          sb.append("var q = Query.topN(\"historytrmx\",");
          sb.append("                 \"ipdestination,bytes\",");
          sb.append("                 \"ipdestination = " + IpAddresses + srcZoneCondition + "\",");
          sb.append("                 \"" + startDate + ", " + endtDate + "\",");
          sb.append("                 \"bytes\",");
          sb.append("                 100000);");
          sb.append("var totalsRcvd = {};");
          sb.append("var t = q.run(");
          sb.append("  function(row,table) {");
          sb.append("    if(row[0]) {");
          sb.append("      totalsRcvd[row[0]] = row[1];");
          sb.append("    }");
          sb.append("  });");
          sb.append("for (var addr in totalsSent) {");
          sb.append("    var TS = 0;");
          sb.append("    var TR = 0;");
          sb.append("    if(totalsSent[addr]) TS = totalsSent[addr];");
          sb.append("    if(totalsRcvd[addr]) TR = totalsRcvd[addr];");
          sb.append("    println(addr + \",\" + TS + \",\" + TR);");
          sb.append("}");
          return sb.toString();
      }
  
      private String getDateString(Date date) {
          DateFormat dfDate = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
          return dfDate.format(date);
      }
  
      @Override
      public void setName(String name) {
          // TODO Auto-generated method stub
  
      }
  
      @Override
      public void setConfigParams(Map<String, Object> params) {
          // TODO Auto-generated method stub
  
      }
  
      @Override
      public Map<String, Object> getConfigParams() {
          // TODO Auto-generated method stub
          return null;
      }
  
      @Override
      public int getRunLevel() {
          // TODO Auto-generated method stub
          return 0;
      }
  
      @Override
      public void setRunLevel(int level) {
          // TODO Auto-generated method stub
  
      }
  }
  