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.security.KeyManagementException;
  import java.security.KeyStoreException;
  import java.security.NoSuchAlgorithmException;
  import java.util.Map;
  
  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.PingCommand;
  import com.cloud.agent.api.StartupCommand;
  import com.cloud.agent.api.StartupNiciraNvpCommand;
  import com.cloud.host.Host;
  import com.cloud.host.Host.Type;
  import com.cloud.network.nicira.ControlClusterStatus;
  import com.cloud.network.nicira.ControlClusterStatus.ClusterRoleConfig;
  import com.cloud.network.nicira.DestinationNatRule;
  import com.cloud.network.nicira.Match;
  import com.cloud.network.nicira.NatRule;
  import com.cloud.network.nicira.NiciraNvpApi;
  import com.cloud.network.nicira.NiciraNvpApiException;
  import com.cloud.network.nicira.SourceNatRule;
  import com.cloud.network.utils.CommandRetryUtility;
  import com.cloud.resource.ServerResource;
  import com.cloud.utils.rest.CloudstackRESTException;
  import com.cloud.utils.rest.HttpClientHelper;
  import com.cloud.utils.nicira.nvp.plugin.NiciraNvpApiVersion;
  
  public class NiciraNvpResource implements ServerResource {
  
      protected Logger logger = LogManager.getLogger(getClass());
  
      public static final int NAME_MAX_LEN = 40;
      public static final int NUM_RETRIES = 2;
      private static final int MAX_REDIRECTS = 5;
  
      private String name;
      private String guid;
      private String zoneId;
  
      private NiciraNvpApi niciraNvpApi;
      private NiciraNvpUtilities niciraNvpUtilities;
      private CommandRetryUtility retryUtility;
  
      protected NiciraNvpApi createNiciraNvpApi(final String host, final String username, final String password) throws CloudstackRESTException {
          try {
              return NiciraNvpApi.create().host(host).username(username).password(password).httpClient(HttpClientHelper.createHttpClient(MAX_REDIRECTS)).build();
          } catch (final KeyManagementException e) {
              throw new CloudstackRESTException("Could not create HTTP client", e);
          } catch (final NoSuchAlgorithmException e) {
              throw new CloudstackRESTException("Could not create HTTP client", e);
          } catch (final KeyStoreException e) {
              throw new CloudstackRESTException("Could not create HTTP client", e);
          }
      }
  
      @Override
      public boolean configure(final String ignoredName, final Map<String, Object> params) throws ConfigurationException {
  
          name = (String) params.get("name");
          if (name == null) {
              throw new ConfigurationException("Unable to find name");
          }
  
          guid = (String) params.get("guid");
          if (guid == null) {
              throw new ConfigurationException("Unable to find the guid");
          }
  
          zoneId = (String) params.get("zoneId");
          if (zoneId == null) {
              throw new ConfigurationException("Unable to find zone");
          }
  
          final String ip = (String) params.get("ip");
          if (ip == null) {
              throw new ConfigurationException("Unable to find IP");
          }
  
          final String adminuser = (String) params.get("adminuser");
          if (adminuser == null) {
              throw new ConfigurationException("Unable to find admin username");
          }
  
          final String adminpass = (String) params.get("adminpass");
          if (adminpass == null) {
              throw new ConfigurationException("Unable to find admin password");
          }
  
          niciraNvpUtilities = NiciraNvpUtilities.getInstance();
          retryUtility = CommandRetryUtility.getInstance();
          retryUtility.setServerResource(this);
  
          try {
              niciraNvpApi = createNiciraNvpApi(ip, adminuser, adminpass);
          } catch (final CloudstackRESTException e) {
              throw new ConfigurationException("Could not create a Nicira Nvp API client: " + e.getMessage());
          }
  
          return true;
      }
  
      public NiciraNvpApi getNiciraNvpApi() {
          return niciraNvpApi;
      }
  
      public NiciraNvpUtilities getNiciraNvpUtilities() {
          return niciraNvpUtilities;
      }
  
      public CommandRetryUtility getRetryUtility() {
          return retryUtility;
      }
  
      @Override
      public boolean start() {
          return true;
      }
  
      @Override
      public boolean stop() {
          return true;
      }
  
      @Override
      public String getName() {
          return name;
      }
  
      @Override
      public Type getType() {
          // Think up a better name for this Type?
          return Host.Type.L2Networking;
      }
  
      @Override
      public StartupCommand[] initialize() {
          final StartupNiciraNvpCommand sc = new StartupNiciraNvpCommand();
          sc.setGuid(guid);
          sc.setName(name);
          sc.setDataCenter(zoneId);
          sc.setPod("");
          sc.setPrivateIpAddress("");
          sc.setStorageIpAddress("");
          sc.setVersion(NiciraNvpResource.class.getPackage().getImplementationVersion());
          return new StartupCommand[] { sc };
      }
  
      @Override
      public PingCommand getCurrentStatus(final long id) {
          try {
              final ControlClusterStatus ccs = niciraNvpApi.getControlClusterStatus();
              getApiProviderMajorityVersion(ccs);
              if (!"stable".equals(ccs.getClusterStatus())) {
                  logger.error("ControlCluster state is not stable: " + ccs.getClusterStatus());
                  return null;
              }
          } catch (final NiciraNvpApiException e) {
              logger.error("getControlClusterStatus failed", e);
              return null;
          }
          return new PingCommand(Host.Type.L2Networking, id);
      }
  
      private void getApiProviderMajorityVersion(ControlClusterStatus ccs) {
          ClusterRoleConfig[] configuredRoles = ccs.getConfiguredRoles();
          if (configuredRoles != null){
              String apiProviderMajorityVersion = searchApiProvider(configuredRoles);
              NiciraNvpApiVersion.setNiciraApiVersion(apiProviderMajorityVersion);
              NiciraNvpApiVersion.logNiciraApiVersion();
          }
      }
  
      private String searchApiProvider(ClusterRoleConfig[] configuredRoles) {
          for (int i = 0; i < configuredRoles.length; i++) {
              if (configuredRoles[i].getRole() != null && configuredRoles[i].getRole().equals("api_provider")){
                  return configuredRoles[i].getMajorityVersion();
              }
          }
          return null;
      }
  
      @Override
      public Answer executeRequest(final Command cmd) {
          final NiciraNvpRequestWrapper wrapper = NiciraNvpRequestWrapper.getInstance();
          try {
              return wrapper.execute(cmd, this);
          } catch (final Exception e) {
              logger.debug("Received unsupported command " + cmd.toString());
              return Answer.createUnsupportedCommandAnswer(cmd);
          }
      }
  
      @Override
      public void disconnected() {
      }
  
      @Override
      public IAgentControl getAgentControl() {
          return null;
      }
  
      @Override
      public void setAgentControl(final IAgentControl agentControl) {
      }
  
      public String natRuleToString(final NatRule rule) {
  
          final StringBuilder natRuleStr = new StringBuilder();
          natRuleStr.append("Rule ");
          natRuleStr.append(rule.getUuid());
          natRuleStr.append(" (");
          natRuleStr.append(rule.getType());
          natRuleStr.append(") :");
          final Match m = rule.getMatch();
          natRuleStr.append("match (");
          natRuleStr.append(m.getProtocol());
          natRuleStr.append(" ");
          natRuleStr.append(m.getSourceIpAddresses());
          natRuleStr.append(" [");
          natRuleStr.append(m.getSourcePort());
          natRuleStr.append(" ] -> ");
          natRuleStr.append(m.getDestinationIpAddresses());
          natRuleStr.append(" [");
          natRuleStr.append(m.getDestinationPort());
          natRuleStr.append(" ]) -->");
          if ("SourceNatRule".equals(rule.getType())) {
              natRuleStr.append(((SourceNatRule) rule).getToSourceIpAddressMin());
              natRuleStr.append("-");
              natRuleStr.append(((SourceNatRule) rule).getToSourceIpAddressMax());
              natRuleStr.append(" [");
              natRuleStr.append(((SourceNatRule) rule).getToSourcePort());
              natRuleStr.append(" ])");
          } else {
              natRuleStr.append(((DestinationNatRule) rule).getToDestinationIpAddress());
              natRuleStr.append(" [");
              natRuleStr.append(((DestinationNatRule) rule).getToDestinationPort());
              natRuleStr.append(" ])");
          }
          return natRuleStr.toString();
      }
  
      public String truncate(final String string, final int length) {
          if (string.length() <= length) {
              return string;
          } else {
              return string.substring(0, length);
          }
      }
  
      public NatRule[] generateStaticNatRulePair(final String insideIp, final String outsideIp) {
          final NatRule[] rulepair = new NatRule[2];
          rulepair[0] = new DestinationNatRule();
          rulepair[0].setType("DestinationNatRule");
          rulepair[0].setOrder(100);
          rulepair[1] = new SourceNatRule();
          rulepair[1].setType("SourceNatRule");
          rulepair[1].setOrder(100);
  
          Match m = new Match();
          m.setDestinationIpAddresses(outsideIp);
          rulepair[0].setMatch(m);
          ((DestinationNatRule) rulepair[0]).setToDestinationIpAddress(insideIp);
  
          // create matching snat rule
          m = new Match();
          m.setSourceIpAddresses(insideIp);
          rulepair[1].setMatch(m);
          ((SourceNatRule) rulepair[1]).setToSourceIpAddressMin(outsideIp);
          ((SourceNatRule) rulepair[1]).setToSourceIpAddressMax(outsideIp);
  
          return rulepair;
  
      }
  
      public NatRule[] generatePortForwardingRulePair(final String insideIp, final int[] insidePorts, final String outsideIp, final int[] outsidePorts,
                      final String protocol) {
          // Start with a basic static nat rule, then add port and protocol details
          final NatRule[] rulepair = generateStaticNatRulePair(insideIp, outsideIp);
  
          ((DestinationNatRule) rulepair[0]).setToDestinationPort(insidePorts[0]);
          rulepair[0].getMatch().setDestinationPort(outsidePorts[0]);
          rulepair[0].setOrder(50);
          rulepair[0].getMatch().setEthertype("IPv4");
          if ("tcp".equals(protocol)) {
              rulepair[0].getMatch().setProtocol(6);
          } else if ("udp".equals(protocol)) {
              rulepair[0].getMatch().setProtocol(17);
          }
  
          ((SourceNatRule) rulepair[1]).setToSourcePort(outsidePorts[0]);
          rulepair[1].getMatch().setSourcePort(insidePorts[0]);
          rulepair[1].setOrder(50);
          rulepair[1].getMatch().setEthertype("IPv4");
          if ("tcp".equals(protocol)) {
              rulepair[1].getMatch().setProtocol(6);
          } else if ("udp".equals(protocol)) {
              rulepair[1].getMatch().setProtocol(17);
          }
  
          return rulepair;
  
      }
  
      @Override
      public void setName(final String name) {
          // TODO Auto-generated method stub
      }
  
      @Override
      public void setConfigParams(final 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(final int level) {
          // TODO Auto-generated method stub
      }
  
  }
  