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 org.apache.cloudstack.network.contrail.management;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import javax.inject.Inject;
  
  import org.apache.cloudstack.network.contrail.api.command.CreateServiceInstanceCmd;
  import org.apache.cloudstack.network.contrail.model.InstanceIpModel;
  import org.apache.cloudstack.network.contrail.model.VMInterfaceModel;
  import org.apache.cloudstack.network.contrail.model.VirtualMachineModel;
  import org.apache.cloudstack.network.contrail.model.VirtualNetworkModel;
  import org.springframework.stereotype.Component;
  
  import com.cloud.deploy.DeployDestination;
  import com.cloud.exception.ConcurrentOperationException;
  import com.cloud.exception.InsufficientCapacityException;
  import com.cloud.exception.ResourceUnavailableException;
  import com.cloud.network.IpAddress;
  import com.cloud.network.Network;
  import com.cloud.network.Network.Capability;
  import com.cloud.network.Network.Provider;
  import com.cloud.network.Network.Service;
  import com.cloud.network.Networks.TrafficType;
  import com.cloud.network.PhysicalNetworkServiceProvider;
  import com.cloud.network.PublicIpAddress;
  import com.cloud.network.dao.NetworkDao;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.network.element.DhcpServiceProvider;
  import com.cloud.network.element.IpDeployer;
  import com.cloud.network.element.SourceNatServiceProvider;
  import com.cloud.network.element.StaticNatServiceProvider;
  import com.cloud.network.rules.StaticNat;
  import com.cloud.offering.NetworkOffering;
  import com.cloud.resource.ResourceManager;
  import com.cloud.server.ConfigurationServer;
  import com.cloud.server.ConfigurationServerImpl;
  import com.cloud.utils.component.AdapterBase;
  import com.cloud.vm.NicProfile;
  import com.cloud.vm.NicVO;
  import com.cloud.vm.ReservationContext;
  import com.cloud.vm.VMInstanceVO;
  import com.cloud.vm.VirtualMachineProfile;
  import com.cloud.vm.dao.NicDao;
  
  
  @Component
  
  public class ContrailElementImpl extends AdapterBase
          implements ContrailElement, StaticNatServiceProvider, IpDeployer, SourceNatServiceProvider, DhcpServiceProvider {
      private final Map<Service, Map<Capability, String>> _capabilities = InitCapabilities();
  
      @Inject
      ResourceManager _resourceMgr;
      @Inject
      ConfigurationServer _configServer;
      @Inject
      NetworkDao _networksDao;
      @Inject
      ContrailManager _manager;
      @Inject
      NicDao _nicDao;
      @Inject
      ServerDBSync _dbSync;
  
      // PluggableService
      @Override
      public List<Class<?>> getCommands() {
          List<Class<?>> cmdList = new ArrayList<Class<?>>();
          cmdList.add(CreateServiceInstanceCmd.class);
          return cmdList;
      }
  
      // NetworkElement API
      @Override
      public Provider getProvider() {
          return Provider.JuniperContrailRouter;
      }
  
      private static Map<Service, Map<Capability, String>> InitCapabilities() {
          Map<Service, Map<Capability, String>> capabilities = new HashMap<Service, Map<Capability, String>>();
          capabilities.put(Service.Connectivity, null);
          capabilities.put(Service.Dhcp, new HashMap<Capability, String>());
          capabilities.put(Service.StaticNat, null);
          capabilities.put(Service.SourceNat, null);
  
          return capabilities;
      }
  
      @Override
      public Map<Service, Map<Capability, String>> getCapabilities() {
          return _capabilities;
      }
  
      /**
       * Network add/update.
       */
      @Override
      public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
              ResourceUnavailableException, InsufficientCapacityException {
          logger.debug(String.format("NetworkElement implement: %s, traffic type: %s", network, network.getTrafficType()));
          if (network.getTrafficType() == TrafficType.Guest) {
              logger.debug(String.format("ignore network %s", network));
              return true;
          }
          VirtualNetworkModel vnModel = _manager.getDatabase().lookupVirtualNetwork(network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType());
  
          if (vnModel == null) {
              vnModel = new VirtualNetworkModel(network, network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType());
              vnModel.setProperties(_manager.getModelController(), network);
          }
          try {
              if (!vnModel.verify(_manager.getModelController())) {
                  vnModel.update(_manager.getModelController());
              }
              _manager.getDatabase().getVirtualNetworks().add(vnModel);
          } catch (Exception ex) {
              logger.warn("virtual-network update: ", ex);
          }
          return true;
      }
  
      @Override
      public boolean prepare(Network network, NicProfile nicProfile, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context)
              throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
  
          logger.debug(String.format("NetworkElement prepare: %s, traffic type: %s", network, network.getTrafficType()));
  
          if (network.getTrafficType() == TrafficType.Guest) {
              logger.debug(String.format("ignore network %s", network));
              return true;
          }
  
          VirtualNetworkModel vnModel = _manager.getDatabase().lookupVirtualNetwork(network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType());
  
          if (vnModel == null) {
              // There is no notification after a physical network is associated with the VRouter NetworkOffering
              // this may be the first time we see this network.
              return false;
          }
  
          VirtualMachineModel vmModel = _manager.getDatabase().lookupVirtualMachine(vm.getUuid());
          if (vmModel == null) {
              VMInstanceVO vmVo = (VMInstanceVO)vm.getVirtualMachine();
              vmModel = new VirtualMachineModel(vmVo, vm.getUuid());
              vmModel.setProperties(_manager.getModelController(), vmVo);
          }
  
          NicVO nic = _nicDao.findById(nicProfile.getId());
          assert nic != null;
  
          VMInterfaceModel vmiModel = vmModel.getVMInterface(nic.getUuid());
          if (vmiModel == null) {
              vmiModel = new VMInterfaceModel(nic.getUuid());
              vmiModel.addToVirtualMachine(vmModel);
              vmiModel.addToVirtualNetwork(vnModel);
          }
  
          try {
              vmiModel.build(_manager.getModelController(), (VMInstanceVO)vm.getVirtualMachine(), nic);
          } catch (IOException ex) {
              logger.warn("vm interface set", ex);
              return false;
          }
  
          InstanceIpModel ipModel = vmiModel.getInstanceIp();
          if (ipModel == null) {
              ipModel = new InstanceIpModel(vm.getInstanceName(), nic.getDeviceId());
              ipModel.addToVMInterface(vmiModel);
          }
          ipModel.setAddress(nicProfile.getIPv4Address());
  
          try {
              vmModel.update(_manager.getModelController());
          } catch (Exception ex) {
              logger.warn("virtual-machine-update", ex);
              return false;
          }
          _manager.getDatabase().getVirtualMachines().add(vmModel);
  
          return true;
      }
  
      @Override
      public boolean release(Network network, NicProfile nicProfile, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException,
              ResourceUnavailableException {
          if (network.getTrafficType() == TrafficType.Guest) {
              return true;
          } else if (!_manager.isManagedPhysicalNetwork(network)) {
              logger.debug(String.format("release ignore network %s", network));
              return true;
          }
  
          NicVO nic = _nicDao.findById(nicProfile.getId());
          assert nic != null;
  
          VirtualMachineModel vmModel = _manager.getDatabase().lookupVirtualMachine(vm.getUuid());
          if (vmModel == null) {
              logger.debug(String.format("vm %s not in local database", vm));
              return true;
          }
          VMInterfaceModel vmiModel = vmModel.getVMInterface(nic.getUuid());
          if (vmiModel != null) {
              try {
                  vmiModel.destroy(_manager.getModelController());
              } catch (IOException ex) {
                  logger.warn("virtual-machine-interface delete", ex);
              }
              vmModel.removeSuccessor(vmiModel);
          }
  
          if (!vmModel.hasDescendents()) {
              _manager.getDatabase().getVirtualMachines().remove(vmModel);
              try {
                  vmModel.delete(_manager.getModelController());
              } catch (IOException e) {
                  return false;
              }
          }
  
          return true;
      }
  
      /**
       * Network disable
       */
      @Override
      public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
          logger.debug("NetworkElement shutdown");
          return true;
      }
  
      /**
       * Network delete
       */
      @Override
      public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
          logger.debug("NetworkElement destroy");
          return true;
      }
  
      @Override
      public boolean isReady(PhysicalNetworkServiceProvider provider) {
          Map<String, String> serviceMap = ((ConfigurationServerImpl)_configServer).getServicesAndProvidersForNetwork( _manager.getRouterOffering().getId());
          List<TrafficType> types = new ArrayList<TrafficType>();
          types.add(TrafficType.Control);
          types.add(TrafficType.Management);
          types.add(TrafficType.Storage);
          List<NetworkVO> systemNets = _manager.findSystemNetworks(types);
          if (systemNets != null && !systemNets.isEmpty()) {
              for (NetworkVO net: systemNets) {
                          logger.debug(String.format("update system network service: %s; service provider: %s", net, serviceMap));
                  _networksDao.update(net.getId(), net, serviceMap);
              }
          } else {
              logger.debug("no system networks created yet");
          }
          serviceMap = ((ConfigurationServerImpl)_configServer).getServicesAndProvidersForNetwork( _manager.getPublicRouterOffering().getId());
          types = new ArrayList<TrafficType>();
          types.add(TrafficType.Public);
          systemNets = _manager.findSystemNetworks(types);
          if (systemNets != null && !systemNets.isEmpty()) {
              for (NetworkVO net: systemNets) {
                          logger.debug(String.format("update system network service: %s; service provider: %s", net, serviceMap));
                  _networksDao.update(net.getId(), net, serviceMap);
              }
          } else {
              logger.debug("no system networks created yet");
          }
          return true;
      }
  
      @Override
      public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException,
              ResourceUnavailableException {
          logger.debug("NetworkElement shutdown ProviderInstances");
          return true;
      }
  
      @Override
      public boolean canEnableIndividualServices() {
          return true;
      }
  
      @Override
      public boolean verifyServicesCombination(Set<Service> services) {
          // TODO Auto-generated method stub
          logger.debug("NetworkElement verifyServices");
          logger.debug("Services: " + services);
          return true;
      }
  
      @Override
      public IpDeployer getIpDeployer(Network network) {
          return this;
      }
  
      @Override
      public boolean applyIps(Network network, List<? extends PublicIpAddress> ipAddress, Set<Service> services) throws ResourceUnavailableException {
  
          for (PublicIpAddress ip : ipAddress) {
              if (ip.isSourceNat()) {
                  continue;
              }
              if (isFloatingIpCreate(ip)) {
                  if (_manager.createFloatingIp(ip)) {
                      logger.debug("Successfully created floating ip: " + ip.getAddress().addr());
                  }
              } else {
                  if (_manager.deleteFloatingIp(ip)) {
                      logger.debug("Successfully deleted floating ip: " + ip.getAddress().addr());
                  }
              }
          }
          return true;
      }
  
      @Override
      public boolean applyStaticNats(Network config, List<? extends StaticNat> rules) throws ResourceUnavailableException {
          return true;
      }
  
      private boolean isFloatingIpCreate(PublicIpAddress ip) {
          if (ip.getState() == IpAddress.State.Allocated && ip.getAssociatedWithVmId() != null && !ip.isSourceNat()) {
              return true;
          }
          return false;
      }
  
      @Override
      public boolean addDhcpEntry(Network network, NicProfile nic,
              VirtualMachineProfile vm,
              DeployDestination dest, ReservationContext context)
              throws ConcurrentOperationException, InsufficientCapacityException,
              ResourceUnavailableException {
          return false;
      }
  
      @Override
      public boolean configDhcpSupportForSubnet(Network network, NicProfile nic,
              VirtualMachineProfile vm,
              DeployDestination dest, ReservationContext context)
              throws ConcurrentOperationException, InsufficientCapacityException,
              ResourceUnavailableException {
          return false;
      }
  
      @Override
      public boolean removeDhcpSupportForSubnet(Network network)
              throws ResourceUnavailableException {
          return false;
      }
  
      @Override
      public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
          return false;
      }
  
      @Override
      public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) {
          return false;
      }
  }
  