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.hypervisor.kvm.resource;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.fail;
  import static org.mockito.ArgumentMatchers.anyMap;
  import static org.mockito.ArgumentMatchers.eq;
  import static org.mockito.Mockito.doReturn;
  import static org.mockito.Mockito.spy;
  
  import java.util.HashMap;
  import java.util.Map;
  
  import javax.naming.ConfigurationException;
  
  import org.junit.Before;
  import org.junit.Test;
  
  import com.cloud.agent.properties.AgentProperties;
  import com.cloud.agent.properties.AgentPropertiesFileHandler;
  import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource.BridgeType;
  import com.cloud.network.Networks.TrafficType;
  import org.junit.runner.RunWith;
  import org.mockito.MockedStatic;
  import org.mockito.Mockito;
  import org.mockito.junit.MockitoJUnitRunner;
  
  @RunWith(MockitoJUnitRunner.class)
  public class LibvirtVifDriverTest {
      private LibvirtComputingResource res;
  
      private Map<TrafficType, VifDriver> assertions;
  
      final String LibVirtVifDriver = "libvirt.vif.driver";
      final String FakeVifDriverClassName = "com.cloud.hypervisor.kvm.resource.FakeVifDriver";
      final String NonExistentVifDriverClassName = "com.cloud.hypervisor.kvm.resource.NonExistentVifDriver";
  
      private VifDriver fakeVifDriver, bridgeVifDriver, ovsVifDriver, tungstenVifDriver;
  
      final String memInfo = "MemTotal:        5830236 kB\n" +
              "MemFree:          156752 kB\n" +
              "Buffers:          326836 kB\n" +
              "Cached:          2606764 kB\n" +
              "SwapCached:            0 kB\n" +
              "Active:          4260808 kB\n" +
              "Inactive:         949392 kB\n";
      @Before
      public void setUp() throws Exception {
          // Use a spy because we only want to override getVifDriverClass
          LibvirtComputingResource resReal = new LibvirtComputingResource();
          res = spy(resReal);
  
          try {
              bridgeVifDriver = (VifDriver)Class.forName(LibvirtComputingResource.DEFAULT_BRIDGE_VIF_DRIVER_CLASS_NAME).newInstance();
              ovsVifDriver = (VifDriver)Class.forName(LibvirtComputingResource.DEFAULT_OVS_VIF_DRIVER_CLASS_NAME).newInstance();
              tungstenVifDriver = (VifDriver)Class.forName(LibvirtComputingResource.DEFAULT_TUNGSTEN_VIF_DRIVER_CLASS_NAME).newInstance();
  
              // Instantiating bridge vif driver again as the fake vif driver
              // is good enough, as this is a separate instance
              fakeVifDriver = (VifDriver)Class.forName(LibvirtComputingResource.DEFAULT_BRIDGE_VIF_DRIVER_CLASS_NAME).newInstance();
  
              doReturn(bridgeVifDriver).when(res).getVifDriverClass(eq(LibvirtComputingResource.DEFAULT_BRIDGE_VIF_DRIVER_CLASS_NAME), anyMap());
              doReturn(ovsVifDriver).when(res).getVifDriverClass(eq(LibvirtComputingResource.DEFAULT_OVS_VIF_DRIVER_CLASS_NAME), anyMap());
              doReturn(tungstenVifDriver).when(res).getVifDriverClass(eq(LibvirtComputingResource.DEFAULT_TUNGSTEN_VIF_DRIVER_CLASS_NAME), anyMap());
              doReturn(fakeVifDriver).when(res).getVifDriverClass(eq(FakeVifDriverClassName), anyMap());
  
          } catch (final ConfigurationException ex) {
              fail("Unexpected ConfigurationException while configuring VIF drivers: " + ex.getMessage());
          } catch (final Exception ex) {
              fail("Unexpected Exception while configuring VIF drivers");
          }
  
          assertions = new HashMap<TrafficType, VifDriver>();
      }
  
      // Helper function
      // Configure LibvirtComputingResource using params
      private void configure(Map<String, Object> params) throws ConfigurationException {
          res.configureVifDrivers(params);
      }
  
      // Helper function
      private void checkAssertions() {
          // Check the defined assertions
          for (Map.Entry<TrafficType, VifDriver> assertion : assertions.entrySet()) {
              assertEquals(res.getVifDriver(assertion.getKey()), assertion.getValue());
          }
      }
  
      // Helper when all answers should be the same
      private void checkAllSame(VifDriver vifDriver) throws ConfigurationException {
  
          for (TrafficType trafficType : TrafficType.values()) {
              assertions.put(trafficType, vifDriver);
          }
  
          checkAssertions();
      }
  
      @Test
      public void testDefaults() throws ConfigurationException {
          // If no special vif driver settings, all traffic types should
          // map to the default vif driver for the bridge type
          Map<String, Object> params = new HashMap<String, Object>();
  
          res.bridgeType = BridgeType.NATIVE;
          configure(params);
          checkAllSame(bridgeVifDriver);
  
          res.bridgeType = BridgeType.OPENVSWITCH;
          configure(params);
          checkAllSame(ovsVifDriver);
      }
  
      @Test
      public void configureVifDriversTestWhenSetEqualToDefault() throws Exception {
          try (MockedStatic<AgentPropertiesFileHandler> agentPropertiesFileHandlerMockedStatic = Mockito.mockStatic(
                  AgentPropertiesFileHandler.class)) {
              Mockito.when(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_VIF_DRIVER)).thenReturn(
                      LibvirtComputingResource.DEFAULT_BRIDGE_VIF_DRIVER_CLASS_NAME,
                      LibvirtComputingResource.DEFAULT_OVS_VIF_DRIVER_CLASS_NAME);
  
              Map<String, Object> params = new HashMap<String, Object>();
  
              // Switch res' bridge type for test purposes
              res.bridgeType = BridgeType.NATIVE;
              configure(params);
              checkAllSame(bridgeVifDriver);
  
              res.bridgeType = BridgeType.OPENVSWITCH;
              configure(params);
              checkAllSame(ovsVifDriver);
  
              agentPropertiesFileHandlerMockedStatic.verify(
                      () -> AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_VIF_DRIVER),
                      Mockito.times(2));
          }
      }
  
      @Test
      public void configureVifDriversTestWhenSetDifferentFromDefault() throws Exception {
          try (MockedStatic<AgentPropertiesFileHandler> agentPropertiesFileHandlerMockedStatic = Mockito.mockStatic(
                  AgentPropertiesFileHandler.class)) {
              Mockito.when(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_VIF_DRIVER))
                     .thenReturn(LibvirtComputingResource.DEFAULT_OVS_VIF_DRIVER_CLASS_NAME,
                             LibvirtComputingResource.DEFAULT_BRIDGE_VIF_DRIVER_CLASS_NAME);
  
              // Tests when explicitly set vif driver to OVS when using regular bridges and vice versa
              Map<String, Object> params = new HashMap<String, Object>();
  
              // Switch res' bridge type for test purposes
              res.bridgeType = BridgeType.NATIVE;
              configure(params);
              checkAllSame(ovsVifDriver);
  
              res.bridgeType = BridgeType.OPENVSWITCH;
              configure(params);
              checkAllSame(bridgeVifDriver);
  
              agentPropertiesFileHandlerMockedStatic.verify(() -> AgentPropertiesFileHandler.getPropertyValue(
                      AgentProperties.LIBVIRT_VIF_DRIVER), Mockito.times(2));
          }
      }
  
      @Test
      public void testOverrideSomeTrafficTypes() throws ConfigurationException {
  
          Map<String, Object> params = new HashMap<String, Object>();
          params.put(LibVirtVifDriver + "." + "Public", FakeVifDriverClassName);
          params.put(LibVirtVifDriver + "." + "Guest", LibvirtComputingResource.DEFAULT_OVS_VIF_DRIVER_CLASS_NAME);
          res.bridgeType = BridgeType.NATIVE;
          configure(params);
  
          // Initially, set all traffic types to use default
          for (TrafficType trafficType : TrafficType.values()) {
              assertions.put(trafficType, bridgeVifDriver);
          }
  
          assertions.put(TrafficType.Public, fakeVifDriver);
          assertions.put(TrafficType.Guest, ovsVifDriver);
  
          checkAssertions();
      }
  
      @Test
      public void testBadTrafficType() throws ConfigurationException {
          Map<String, Object> params = new HashMap<String, Object>();
          params.put(LibVirtVifDriver + "." + "NonExistentTrafficType", FakeVifDriverClassName);
          res.bridgeType = BridgeType.NATIVE;
          configure(params);
  
          // Set all traffic types to use default, because bad traffic type should be ignored
          for (TrafficType trafficType : TrafficType.values()) {
              assertions.put(trafficType, bridgeVifDriver);
          }
  
          checkAssertions();
      }
  
      @Test
      public void testEmptyTrafficType() throws ConfigurationException {
          Map<String, Object> params = new HashMap<String, Object>();
          params.put(LibVirtVifDriver + ".", FakeVifDriverClassName);
          res.bridgeType = BridgeType.NATIVE;
          configure(params);
  
          // Set all traffic types to use default, because bad traffic type should be ignored
          for (TrafficType trafficType : TrafficType.values()) {
              assertions.put(trafficType, bridgeVifDriver);
          }
  
          checkAssertions();
      }
  
      @Test(expected = ConfigurationException.class)
      public void testBadVifDriverClassName() throws ConfigurationException {
          Map<String, Object> params = new HashMap<String, Object>();
          params.put(LibVirtVifDriver + "." + "Public", NonExistentVifDriverClassName);
          res.bridgeType = BridgeType.NATIVE;
          configure(params);
      }
  }
  