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;
  
  import java.util.List;
  import java.util.Map;
  
  import javax.inject.Inject;
  import javax.naming.ConfigurationException;
  
  import org.springframework.stereotype.Component;
  
  import com.cloud.agent.AgentManager;
  import com.cloud.agent.StartupCommandProcessor;
  import com.cloud.agent.api.StartupCommand;
  import com.cloud.agent.api.StartupRoutingCommand;
  import com.cloud.agent.api.StartupStorageCommand;
  import com.cloud.agent.manager.authn.AgentAuthnException;
  import com.cloud.dc.DataCenterVO;
  import com.cloud.dc.HostPodVO;
  import com.cloud.dc.dao.DataCenterDao;
  import com.cloud.dc.dao.HostPodDao;
  import com.cloud.host.Host;
  import com.cloud.host.HostVO;
  import com.cloud.hypervisor.Hypervisor.HypervisorType;
  import com.cloud.utils.component.AdapterBase;
  import com.cloud.utils.net.MacAddress;
  import com.cloud.utils.net.NetUtils;
  
  /**
   * Creates a host record and supporting records such as pod and ip address
   *
   */
  @Component
  public class CloudZonesStartupProcessor extends AdapterBase implements StartupCommandProcessor {
      @Inject
      private DataCenterDao _zoneDao = null;
      @Inject
      private HostPodDao _podDao = null;
      @Inject
      private AgentManager _agentManager = null;
  
      private long _nodeId = -1;
  
      @Override
      public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
          _agentManager.registerForInitialConnects(this, false);
          if (_nodeId == -1) {
              // FIXME: We really should not do this like this. It should be done
              // at config time and is stored as a config variable.
              _nodeId = MacAddress.getMacAddress().toLong();
          }
          return true;
      }
  
      @Override
      public boolean processInitialConnect(StartupCommand[] cmd) {
          StartupCommand startup = cmd[0];
          return startup instanceof StartupRoutingCommand || startup instanceof StartupStorageCommand;
      }
  
      private boolean checkCIDR(Host.Type type, HostPodVO pod, String serverPrivateIP, String serverPrivateNetmask) {
          if (serverPrivateIP == null) {
              return true;
          }
          // Get the CIDR address and CIDR size
          String cidrAddress = pod.getCidrAddress();
          long cidrSize = pod.getCidrSize();
  
          // If the server's private IP address is not in the same subnet as the
          // pod's CIDR, return false
          String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSize);
          String serverSubnet = NetUtils.getSubNet(serverPrivateIP, serverPrivateNetmask);
          if (!cidrSubnet.equals(serverSubnet)) {
              return false;
          }
          return true;
      }
  
      private void updatePodNetmaskIfNeeded(HostPodVO pod, String agentNetmask) {
          // If the server's private netmask is less inclusive than the pod's CIDR
          // netmask, update cidrSize of the default POD
          //(reason: we are maintaining pods only for internal accounting.)
          long cidrSize = pod.getCidrSize();
          String cidrNetmask = NetUtils.getCidrSubNet("255.255.255.255", cidrSize);
          long cidrNetmaskNumeric = NetUtils.ip2Long(cidrNetmask);
          long serverNetmaskNumeric = NetUtils.ip2Long(agentNetmask);//
          if (serverNetmaskNumeric > cidrNetmaskNumeric) {
              //update pod's cidrsize
              int newCidrSize = new Long(NetUtils.getCidrSize(agentNetmask)).intValue();
              pod.setCidrSize(newCidrSize);
              _podDao.update(pod.getId(), pod);
          }
      }
  
      protected void updateSecondaryHost(final HostVO host, final StartupStorageCommand startup, final Host.Type type) throws AgentAuthnException {
  
          String zoneToken = startup.getDataCenter();
          if (zoneToken == null) {
              logger.warn("No Zone Token passed in, cannot not find zone for the agent");
              throw new AgentAuthnException("No Zone Token passed in, cannot not find zone for agent");
          }
  
          DataCenterVO zone = _zoneDao.findByToken(zoneToken);
          if (zone == null) {
              zone = _zoneDao.findByName(zoneToken);
              if (zone == null) {
                  try {
                      long zoneId = Long.parseLong(zoneToken);
                      zone = _zoneDao.findById(zoneId);
                      if (zone == null) {
                          throw new AgentAuthnException("Could not find zone for agent with token " + zoneToken);
                      }
                  } catch (NumberFormatException nfe) {
                      throw new AgentAuthnException("Could not find zone for agent with token " + zoneToken);
                  }
              }
          }
          if (logger.isDebugEnabled()) {
              logger.debug("Successfully loaded the DataCenter {} from the zone token passed in ", zone);
          }
  
          HostPodVO pod = findPod(startup, zone.getId(), Host.Type.Routing); //yes, routing
          Long podId = null;
          if (pod != null) {
              logger.debug("Found pod {} for the secondary storage host {}", pod, startup.getName());
              podId = pod.getId();
          }
          host.setDataCenterId(zone.getId());
          host.setPodId(podId);
          host.setClusterId(null);
          host.setPrivateIpAddress(startup.getPrivateIpAddress());
          host.setPrivateNetmask(startup.getPrivateNetmask());
          host.setPrivateMacAddress(startup.getPrivateMacAddress());
          host.setPublicIpAddress(startup.getPublicIpAddress());
          host.setPublicMacAddress(startup.getPublicMacAddress());
          host.setPublicNetmask(startup.getPublicNetmask());
          host.setStorageIpAddress(startup.getStorageIpAddress());
          host.setStorageMacAddress(startup.getStorageMacAddress());
          host.setStorageNetmask(startup.getStorageNetmask());
          host.setVersion(startup.getVersion());
          host.setName(startup.getName());
          host.setType(type);
          host.setStorageUrl(startup.getIqn());
          host.setLastPinged(System.currentTimeMillis() >> 10);
          host.setCaps(null);
          host.setCpus(null);
          host.setTotalMemory(0);
          host.setSpeed(null);
          host.setParent(startup.getParent());
          host.setTotalSize(startup.getTotalSize());
          host.setHypervisorType(HypervisorType.None);
          if (startup.getNfsShare() != null) {
              host.setStorageUrl(startup.getNfsShare());
          }
  
      }
  
      private HostPodVO findPod(StartupCommand startup, long zoneId, Host.Type type) {
          HostPodVO pod = null;
          List<HostPodVO> podsInZone = _podDao.listByDataCenterId(zoneId);
          for (HostPodVO hostPod : podsInZone) {
              if (checkCIDR(type, hostPod, startup.getPrivateIpAddress(), startup.getPrivateNetmask())) {
                  pod = hostPod;
  
                  //found the default POD having the same subnet.
                  updatePodNetmaskIfNeeded(pod, startup.getPrivateNetmask());
  
                  break;
              }
          }
          return pod;
  
      }
  
  }
  