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.hyperv.guru;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
  import java.util.Map;
  
  import javax.inject.Inject;
  
  import com.cloud.agent.api.to.NicTO;
  import com.cloud.agent.api.to.VirtualMachineTO;
  import com.cloud.exception.InsufficientAddressCapacityException;
  import com.cloud.hypervisor.Hypervisor.HypervisorType;
  import com.cloud.hypervisor.HypervisorGuru;
  import com.cloud.hypervisor.HypervisorGuruBase;
  import com.cloud.hypervisor.hyperv.manager.HypervManager;
  import com.cloud.network.NetworkModel;
  import com.cloud.network.Networks.BroadcastDomainType;
  import com.cloud.network.Networks.TrafficType;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.storage.GuestOSVO;
  import com.cloud.storage.dao.GuestOSDao;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.vm.NicProfile;
  import com.cloud.vm.VirtualMachine;
  import com.cloud.vm.VirtualMachineProfile;
  
  /**
   * Implementation of Hypervisor guru for Hyper-V.
   **/
  public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru {
  
      @Inject
      private GuestOSDao _guestOsDao;
      @Inject HypervManager _hypervMgr;
      @Inject NetworkModel networkModel;
      int MaxNicSupported = 8;
      @Override
      public final HypervisorType getHypervisorType() {
          return HypervisorType.Hyperv;
      }
  
      /**
       * Prevent direct creation.
       */
      protected HypervGuru() {
          super();
      }
  
      @Override
      public final VirtualMachineTO implement(VirtualMachineProfile vm) {
          VirtualMachineTO to = toVirtualMachineTO(vm);
          List<NicProfile> nicProfiles = vm.getNics();
  
          if(vm.getVirtualMachine().getType() ==  VirtualMachine.Type.DomainRouter) {
  
              NicProfile publicNicProfile = null;
              NicProfile controlNicProfile = null;
              NicProfile profile = null;
              for(NicProfile nicProfile : nicProfiles) {
                  if(nicProfile.getTrafficType() == TrafficType.Public) {
                      publicNicProfile = nicProfile;
                      break;
                  }
                  else if (nicProfile.getTrafficType() == TrafficType.Control) {
                      controlNicProfile = nicProfile;
                  }
              }
  
              if(publicNicProfile != null || controlNicProfile != null) {
                  NicTO[] nics = to.getNics();
                  // reserve extra NICs
                  NicTO[] expandedNics = new NicTO[MaxNicSupported];
                  int i = 0;
                  int deviceId = -1;
                  for(i = 0; i < nics.length; i++) {
                      expandedNics[i] = nics[i];
                      if(nics[i].getDeviceId() > deviceId)
                          deviceId = nics[i].getDeviceId();
                  }
                  deviceId++;
  
                  long networkId = 0;
                  if(publicNicProfile != null ) {
                      networkId= publicNicProfile.getNetworkId();
                      profile = publicNicProfile;
                  }
                  else {
                      networkId =  controlNicProfile.getNetworkId();
                      profile = controlNicProfile;
                  }
  
                  NetworkVO network = networkDao.findById(networkId);
                  // for Hyperv Hot Nic plug is not supported and it will support upto 8 nics.
                  // creating the VR with extra nics (actual nics(3) + extra nics) will be 8
                  for(; i < MaxNicSupported; i++) {
                      NicTO nicTo = new NicTO();
                      nicTo.setDeviceId(deviceId++);
                      nicTo.setBroadcastType(BroadcastDomainType.Vlan);
                      nicTo.setType(TrafficType.Public);
                      nicTo.setIp("0.0.0.0");
                      nicTo.setNetmask("255.255.255.255");
                      nicTo.setName(profile.getName());
  
                      try {
                          String mac = networkModel.getNextAvailableMacAddressInNetwork(networkId);
                          nicTo.setMac(mac);
                      } catch (InsufficientAddressCapacityException e) {
                          throw new CloudRuntimeException(String.format("unable to allocate mac address on network: %s", network.getUuid()));
                      }
                      nicTo.setDns1(profile.getIPv4Dns1());
                      nicTo.setDns2(profile.getIPv4Dns2());
                      if (publicNicProfile != null && publicNicProfile.getIPv4Gateway() != null) {
                          nicTo.setGateway(publicNicProfile.getIPv4Gateway());
                      } else {
                          nicTo.setGateway(network.getGateway());
                      }
                      nicTo.setDefaultNic(false);
                      nicTo.setBroadcastUri(profile.getBroadCastUri());
                      nicTo.setIsolationuri(profile.getIsolationUri());
  
                      Integer networkRate = networkModel.getNetworkRate(network.getId(), null);
                      nicTo.setNetworkRateMbps(networkRate);
  
                      expandedNics[i] = nicTo;
                  }
                  to.setNics(expandedNics);
              }
  
              StringBuffer sbMacSequence = new StringBuffer();
              for(NicTO nicTo : sortNicsByDeviceId(to.getNics())) {
                  sbMacSequence.append(nicTo.getMac()).append("|");
              }
  
              if (!sbMacSequence.toString().isEmpty()) {
                  sbMacSequence.deleteCharAt(sbMacSequence.length() - 1);
                  String bootArgs = to.getBootArgs();
                  to.setBootArgs(bootArgs + " nic_macs=" + sbMacSequence.toString());
              }
  
          }
  
          // Determine the VM's OS description
          GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine().getGuestOSId());
          to.setOs(guestOS.getDisplayName());
  
          return to;
      }
  
      private NicTO[] sortNicsByDeviceId(NicTO[] nics) {
  
          List<NicTO> listForSort = new ArrayList<NicTO>();
          for (NicTO nic : nics) {
              listForSort.add(nic);
          }
          Collections.sort(listForSort, new Comparator<NicTO>() {
  
              @Override
              public int compare(NicTO arg0, NicTO arg1) {
                  if (arg0.getDeviceId() < arg1.getDeviceId()) {
                      return -1;
                  } else if (arg0.getDeviceId() == arg1.getDeviceId()) {
                      return 0;
                  }
  
                  return 1;
              }
          });
  
          return listForSort.toArray(new NicTO[0]);
      }
  
      @Override
      public final boolean trackVmHostChange() {
          return false;
      }
  
      @Override
      public Map<String, String> getClusterSettings(long vmId) {
          return null;
      }
  
  }
  