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.xenserver.discoverer;
  
  import java.net.InetAddress;
  import java.net.URI;
  import java.net.UnknownHostException;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Queue;
  import java.util.Set;
  
  import javax.inject.Inject;
  import javax.naming.ConfigurationException;
  import javax.persistence.EntityExistsException;
  
  import com.cloud.hypervisor.xenserver.resource.XcpServer83Resource;
  import com.cloud.hypervisor.xenserver.resource.Xenserver84Resource;
  import org.apache.cloudstack.hypervisor.xenserver.XenserverConfigs;
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.maven.artifact.versioning.ComparableVersion;
  import org.apache.xmlrpc.XmlRpcException;
  
  import com.cloud.agent.AgentManager;
  import com.cloud.agent.Listener;
  import com.cloud.agent.api.AgentControlAnswer;
  import com.cloud.agent.api.AgentControlCommand;
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.Command;
  import com.cloud.agent.api.SetupAnswer;
  import com.cloud.agent.api.SetupCommand;
  import com.cloud.agent.api.StartupCommand;
  import com.cloud.agent.api.StartupRoutingCommand;
  import com.cloud.alert.AlertManager;
  import com.cloud.configuration.Config;
  import com.cloud.dc.ClusterVO;
  import com.cloud.dc.DataCenterVO;
  import com.cloud.dc.HostPodVO;
  import com.cloud.dc.dao.HostPodDao;
  import com.cloud.exception.AgentUnavailableException;
  import com.cloud.exception.ConnectionException;
  import com.cloud.exception.DiscoveredWithErrorException;
  import com.cloud.exception.DiscoveryException;
  import com.cloud.exception.OperationTimedoutException;
  import com.cloud.host.HostEnvironment;
  import com.cloud.host.HostInfo;
  import com.cloud.host.HostVO;
  import com.cloud.host.Status;
  import com.cloud.hypervisor.Hypervisor;
  import com.cloud.hypervisor.Hypervisor.HypervisorType;
  import com.cloud.hypervisor.xenserver.resource.CitrixHelper;
  import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
  import com.cloud.hypervisor.xenserver.resource.XcpOssResource;
  import com.cloud.hypervisor.xenserver.resource.XcpServerResource;
  import com.cloud.hypervisor.xenserver.resource.XenServer56FP1Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer56Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer56SP2Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer600Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer620Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer620SP1Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServer650Resource;
  import com.cloud.hypervisor.xenserver.resource.XenServerConnectionPool;
  import com.cloud.hypervisor.xenserver.resource.Xenserver625Resource;
  import com.cloud.resource.Discoverer;
  import com.cloud.resource.DiscovererBase;
  import com.cloud.resource.ResourceStateAdapter;
  import com.cloud.resource.ServerResource;
  import com.cloud.resource.UnableDeleteHostException;
  import com.cloud.storage.Storage.ImageFormat;
  import com.cloud.storage.Storage.TemplateType;
  import com.cloud.storage.VMTemplateVO;
  import com.cloud.storage.dao.VMTemplateDao;
  import com.cloud.template.TemplateManager;
  import com.cloud.user.Account;
  import com.cloud.utils.NumbersUtil;
  import com.cloud.utils.db.QueryBuilder;
  import com.cloud.utils.db.SearchCriteria.Op;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.utils.exception.HypervisorVersionChangedException;
  import com.xensource.xenapi.Connection;
  import com.xensource.xenapi.Host;
  import com.xensource.xenapi.HostPatch;
  import com.xensource.xenapi.Pool;
  import com.xensource.xenapi.PoolPatch;
  import com.xensource.xenapi.Session;
  import com.xensource.xenapi.Types.SessionAuthenticationFailed;
  import com.xensource.xenapi.Types.UuidInvalid;
  import com.xensource.xenapi.Types.XenAPIException;
  
  
  public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, Listener, ResourceStateAdapter {
      private int _wait;
      private XenServerConnectionPool _connPool;
      private boolean _checkHvm;
      private boolean _setupMultipath;
      private String _instance;
  
      @Inject
      private AlertManager _alertMgr;
      @Inject
      private AgentManager _agentMgr;
      @Inject
      private VMTemplateDao _tmpltDao;
      @Inject
      private HostPodDao _podDao;
  
      private String xenServerIsoName = TemplateManager.XS_TOOLS_ISO;
      private String xenServerIsoDisplayText = "XenServer Tools Installer ISO (xen-pv-drv-iso)";
  
      public final static String MIN_UEFI_SUPPORTED_VERSION = "8.2";
  
      public static boolean isUefiSupported(String hostProductVersion) {
          if (StringUtils.isEmpty(hostProductVersion)) {
              return false;
          }
          ComparableVersion version = new ComparableVersion(hostProductVersion);
          return version.compareTo(new ComparableVersion(MIN_UEFI_SUPPORTED_VERSION)) >= 0;
      }
  
      protected XcpServerDiscoverer() {
      }
  
      void setClusterGuid(ClusterVO cluster, String guid) {
          cluster.setGuid(guid);
          try {
              _clusterDao.update(cluster.getId(), cluster);
          } catch (EntityExistsException e) {
              QueryBuilder<ClusterVO> sc = QueryBuilder.create(ClusterVO.class);
              sc.and(sc.entity().getGuid(), Op.EQ, guid);
              List<ClusterVO> clusters = sc.list();
              ClusterVO clu = clusters.get(0);
              List<Long> clusterHostIds = _hostDao.listIdsByClusterId(clu.getId());
              if (CollectionUtils.isEmpty(clusterHostIds)) {
                  clu.setGuid(null);
                  _clusterDao.update(clu.getId(), clu);
                  _clusterDao.update(cluster.getId(), cluster);
                  return;
              }
              throw e;
          }
      }
  
      protected boolean poolHasHotFix(Connection conn, String hostIp, String hotFixUuid) {
          try {
              Map<Host, Host.Record> hosts = Host.getAllRecords(conn);
              for (Map.Entry<Host, Host.Record> entry : hosts.entrySet()) {
  
                  Host.Record re = entry.getValue();
                  if (!re.address.equalsIgnoreCase(hostIp)){
                      continue;
                  }
                  Set<HostPatch> patches = re.patches;
                  PoolPatch poolPatch = PoolPatch.getByUuid(conn, hotFixUuid);
                  for(HostPatch patch : patches) {
                      PoolPatch pp = patch.getPoolPatch(conn);
                      if (pp != null && pp.equals(poolPatch) && patch.getApplied(conn)) {
                          logger.debug("host " + hostIp + " does have " + hotFixUuid +" Hotfix.");
                          return true;
                      }
                  }
              }
              return false;
          } catch (UuidInvalid e) {
              logger.debug("host " + hostIp + " doesn't have " + hotFixUuid + " Hotfix");
          } catch (Exception e) {
              logger.debug("can't get patches information, consider it doesn't have " + hotFixUuid + " Hotfix");
          }
          return false;
      }
  
  
  
      @Override
      public Map<? extends ServerResource, Map<String, String>>
      find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List<String> hostTags) throws DiscoveryException {
          Map<CitrixResourceBase, Map<String, String>> resources = new HashMap<CitrixResourceBase, Map<String, String>>();
          Connection conn = null;
          if (!url.getScheme().equals("http")) {
              String msg = "urlString is not http so we're not taking care of the discovery for this: " + url;
              logger.debug(msg);
              return null;
          }
          if (clusterId == null) {
              String msg = "must specify cluster Id when add host";
              logger.debug(msg);
              throw new RuntimeException(msg);
          }
  
          if (podId == null) {
              String msg = "must specify pod Id when add host";
              logger.debug(msg);
              throw new RuntimeException(msg);
          }
  
          ClusterVO cluster = _clusterDao.findById(clusterId);
          if (cluster == null || cluster.getHypervisorType() != HypervisorType.XenServer) {
              if (logger.isInfoEnabled()) {
                  logger.info("invalid cluster id or cluster is not for XenServer hypervisors");
              }
              return null;
          }
  
          try {
              String hostname = url.getHost();
              InetAddress ia = InetAddress.getByName(hostname);
              String hostIp = ia.getHostAddress();
              Queue<String> pass = new LinkedList<String>();
              pass.add(password);
              conn = _connPool.getConnect(hostIp, username, pass);
              if (conn == null) {
                  String msg = "Unable to get a connection to " + url;
                  logger.debug(msg);
                  throw new DiscoveryException(msg);
              }
  
              Set<Pool> pools = Pool.getAll(conn);
              Pool pool = pools.iterator().next();
              Pool.Record pr = pool.getRecord(conn);
              String poolUuid = pr.uuid;
              Map<Host, Host.Record> hosts = Host.getAllRecords(conn);
              String latestHotFix = "";
              if (poolHasHotFix(conn, hostIp, XenserverConfigs.XSHotFix62ESP1004)) {
                  latestHotFix = XenserverConfigs.XSHotFix62ESP1004;
              } else if (poolHasHotFix(conn, hostIp, XenserverConfigs.XSHotFix62ESP1)) {
                  latestHotFix = XenserverConfigs.XSHotFix62ESP1;
              }
  
              /*set cluster hypervisor type to xenserver*/
              ClusterVO clu = _clusterDao.findById(clusterId);
              if (clu.getGuid() == null) {
                  setClusterGuid(clu, poolUuid);
              } else {
                  List<Long> clusterHostIds = _hostDao.listIdsByClusterId(clusterId);
                  if (CollectionUtils.isNotEmpty(clusterHostIds)) {
                      if (!clu.getGuid().equals(poolUuid)) {
                          String msg = "Please join the host " +  hostIp + " to XS pool  "
                                  + clu.getGuid() + " through XC/XS before adding it through CS UI";
                          logger.warn(msg);
                          throw new DiscoveryException(msg);
                      }
                  } else {
                      setClusterGuid(clu, poolUuid);
                  }
              }
              // can not use this conn after this point, because this host may join a pool, this conn is retired
              if (conn != null) {
                  try {
                      Session.logout(conn);
                  } catch (Exception e) {
                      logger.debug("Caught exception during logout", e);
                  }
                  conn = null;
              }
  
              poolUuid = clu.getGuid();
              _clusterDao.update(clusterId, clu);
  
              if (_checkHvm) {
                  for (Map.Entry<Host, Host.Record> entry : hosts.entrySet()) {
                      Host.Record record = entry.getValue();
  
                      boolean support_hvm = false;
                      for (String capability : record.capabilities) {
                          if (capability.contains("hvm")) {
                              support_hvm = true;
                              break;
                          }
                      }
                      if (!support_hvm) {
                          String msg = "Unable to add host " + record.address + " because it doesn't support hvm";
                          _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, dcId, podId, msg, msg);
                          logger.debug(msg);
                          throw new RuntimeException(msg);
                      }
                  }
              }
  
              for (Map.Entry<Host, Host.Record> entry : hosts.entrySet()) {
                  Host.Record record = entry.getValue();
                  String hostAddr = record.address;
  
                  String prodVersion = CitrixHelper.getProductVersion(record);
                  String xenVersion = record.softwareVersion.get("xen");
                  String hostOS = record.softwareVersion.get("product_brand");
                  if (hostOS == null) {
                      hostOS = record.softwareVersion.get("platform_name");
                  }
  
                  String hostOSVer = prodVersion;
                  String hostKernelVer = record.softwareVersion.get("linux");
  
                  if (_resourceMgr.findHostByGuid(record.uuid) != null) {
                      logger.debug("Skipping " + record.address + " because " + record.uuid + " is already in the database.");
                      continue;
                  }
  
                  CitrixResourceBase resource = createServerResource(dcId, podId, record, latestHotFix);
                  logger.info("Found host " + record.hostname + " ip=" + record.address + " product version=" + prodVersion);
  
                  Map<String, String> details = new HashMap<String, String>();
                  Map<String, Object> params = new HashMap<String, Object>();
                  details.put("url", hostAddr);
                  details.put("username", username);
                  params.put("username", username);
                  details.put("password", password);
                  params.put("password", password);
                  params.put("zone", Long.toString(dcId));
                  params.put("guid", record.uuid);
                  params.put("pod", podId.toString());
                  params.put("cluster", clusterId.toString());
                  params.put("pool", poolUuid);
                  params.put("ipaddress", record.address);
  
                  details.put(HostInfo.HOST_OS, hostOS);
                  details.put(HostInfo.HOST_OS_VERSION, hostOSVer);
                  details.put(HostInfo.HOST_OS_KERNEL_VERSION, hostKernelVer);
                  details.put(HostInfo.HYPERVISOR_VERSION, xenVersion);
  
                  String privateNetworkLabel = _networkMgr.getDefaultManagementTrafficLabel(dcId, HypervisorType.XenServer);
                  String storageNetworkLabel = _networkMgr.getDefaultStorageTrafficLabel(dcId, HypervisorType.XenServer);
  
                  if (!params.containsKey("private.network.device") && privateNetworkLabel != null) {
                      params.put("private.network.device", privateNetworkLabel);
                      details.put("private.network.device", privateNetworkLabel);
                  }
  
                  if (!params.containsKey("storage.network.device1") && storageNetworkLabel != null) {
                      params.put("storage.network.device1", storageNetworkLabel);
                      details.put("storage.network.device1", storageNetworkLabel);
                  }
  
                  DataCenterVO zone = _dcDao.findById(dcId);
                  boolean securityGroupEnabled = zone.isSecurityGroupEnabled() || _networkMgr.isSecurityGroupSupportedForZone(zone.getId());
                  params.put("securitygroupenabled", Boolean.toString(securityGroupEnabled));
  
                  params.put("router.aggregation.command.each.timeout", _configDao.getValue(Config.RouterAggregationCommandEachTimeout.toString()));
                  params.put("wait", Integer.toString(_wait));
                  details.put("wait", Integer.toString(_wait));
                  params.put("migratewait", _configDao.getValue(Config.MigrateWait.toString()));
                  params.put(Config.XenServerMaxNics.toString().toLowerCase(), _configDao.getValue(Config.XenServerMaxNics.toString()));
                  params.put(Config.XenServerHeartBeatTimeout.toString().toLowerCase(), _configDao.getValue(Config.XenServerHeartBeatTimeout.toString()));
                  params.put(Config.XenServerHeartBeatInterval.toString().toLowerCase(), _configDao.getValue(Config.XenServerHeartBeatInterval.toString()));
                  params.put(Config.InstanceName.toString().toLowerCase(), _instance);
                  details.put(Config.InstanceName.toString().toLowerCase(), _instance);
                  try {
                      resource.configure("XenServer", params);
                  } catch (ConfigurationException e) {
                      _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, dcId, podId, "Unable to add " + record.address, "Error is " + e.getMessage());
                      logger.warn("Unable to instantiate " + record.address, e);
                      continue;
                  }
                  resource.start();
                  resources.put(resource, details);
              }
          } catch (SessionAuthenticationFailed e) {
              throw new DiscoveredWithErrorException("Authentication error");
          } catch (XenAPIException e) {
              logger.warn("XenAPI exception", e);
              return null;
          } catch (XmlRpcException e) {
              logger.warn("Xml Rpc Exception", e);
              return null;
          } catch (UnknownHostException e) {
              logger.warn("Unable to resolve the host name", e);
              return null;
          } catch (Exception e) {
              logger.warn("other exceptions: " + e.toString(), e);
              return null;
          }
          return resources;
      }
  
      String getPoolUuid(Connection conn) throws XenAPIException, XmlRpcException {
          Map<Pool, Pool.Record> pools = Pool.getAllRecords(conn);
          assert pools.size() == 1 : "Pools size is " + pools.size();
          return pools.values().iterator().next().uuid;
      }
  
      protected void addSamePool(Connection conn, Map<CitrixResourceBase, Map<String, String>> resources) throws XenAPIException, XmlRpcException {
          Map<Pool, Pool.Record> hps = Pool.getAllRecords(conn);
          assert (hps.size() == 1) : "How can it be more than one but it's actually " + hps.size();
  
          // This is the pool.
          String poolUuid = hps.values().iterator().next().uuid;
  
          for (Map<String, String> details : resources.values()) {
              details.put("pool", poolUuid);
          }
      }
  
      protected CitrixResourceBase createServerResource(String prodBrand, String prodVersion, String prodVersionTextShort, String hotfix) {
          // Xen Cloud Platform group of hypervisors
          if (prodBrand.equals("XCP") && (prodVersion.equals("1.0.0") || prodVersion.equals("1.1.0")
                  || prodVersion.equals("5.6.100") || prodVersion.startsWith("1.4") || prodVersion.startsWith("1.6"))) {
              return new XcpServerResource();
          } // Citrix Xenserver group of hypervisors
          else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.0")) {
              return new XenServer56Resource();
          } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.0.0")) {
              return new XenServer600Resource();
          } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.0.2")) {
              return new XenServer600Resource();
          } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.1.0")) {
              return new XenServer610Resource();
          } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.2.0")) {
              if (hotfix != null && hotfix.equals(XenserverConfigs.XSHotFix62ESP1004)) {
                  return new Xenserver625Resource();
              } else if (hotfix != null && hotfix.equals(XenserverConfigs.XSHotFix62ESP1)) {
                  return new XenServer620SP1Resource();
              } else {
                  return new XenServer620Resource();
              }
          } else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.100")) {
              if ("5.6 SP2".equals(prodVersionTextShort.trim())) {
                  return new XenServer56SP2Resource();
              } else if ("5.6 FP1".equals(prodVersionTextShort.trim())) {
                  return new XenServer56FP1Resource();
              }
          } else if (prodBrand.equals("XCP_Kronos")) {
              return new XcpOssResource();
          } else if (prodBrand.equals("XenServer") && prodVersion.equals("8.4.0")) {
              return new Xenserver84Resource();
          }  else if (prodBrand.equals("XCP-ng") && (prodVersion.equals("8.3.0"))) {
              return new XcpServer83Resource();
          } else if (prodBrand.equals("XenServer") || prodBrand.equals("XCP-ng") || prodBrand.equals("Citrix Hypervisor")) {
              final String[] items = prodVersion.split("\\.");
              if ((Integer.parseInt(items[0]) > 6) ||
                      (Integer.parseInt(items[0]) == 6 && Integer.parseInt(items[1]) >= 4)) {
                  logger.warn("defaulting to xenserver650 resource for product brand: " + prodBrand + " with product " +
                          "version: " + prodVersion);
                  //default to xenserver650 resource.
                  return new XenServer650Resource();
              }
          }
          String msg =
                  "Only support XCP 1.0.0, 1.1.0, 1.4.x, 1.5 beta, 1.6.x; XenServer 5.6,  XenServer 5.6 FP1, XenServer 5.6 SP2, Xenserver 6.0, 6.0.2, 6.1.0, 6.2.0, >6.4.0, Citrix Hypervisor > 8.0.0 but this one is " +
                          prodBrand + " " + prodVersion;
          logger.warn(msg);
          throw new RuntimeException(msg);
      }
  
  
  
      protected CitrixResourceBase createServerResource(long dcId, Long podId, Host.Record record, String hotfix) {
          String prodBrand = record.softwareVersion.get("product_brand");
          if (prodBrand == null) {
              prodBrand = record.softwareVersion.get("platform_name").trim();
          } else {
              prodBrand = prodBrand.trim();
          }
          String prodVersion = CitrixHelper.getProductVersion(record);
  
          String prodVersionTextShort = record.softwareVersion.get("product_version_text_short");
          return createServerResource(prodBrand, prodVersion, prodVersionTextShort, hotfix);
      }
  
      protected void serverConfig() {
          String value = _params.get(Config.XenServerSetupMultipath.key());
          _setupMultipath = Boolean.parseBoolean(value);
      }
  
      @Override
      public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
          super.configure(name, params);
          serverConfig();
  
          String value = _params.get(Config.XapiWait.toString());
          _wait = NumbersUtil.parseInt(value, Integer.parseInt(Config.XapiWait.getDefaultValue()));
  
          _instance = _params.get(Config.InstanceName.key());
  
          value = _params.get("xenserver.check.hvm");
          _checkHvm = Boolean.parseBoolean(value);
          _connPool = XenServerConnectionPool.getInstance();
  
          _agentMgr.registerForHostEvents(this, true, false, true);
  
          createXenServerToolsIsoEntryInDatabase();
          _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this);
          return true;
      }
  
      @Override
      public boolean matchHypervisor(String hypervisor) {
          if (hypervisor == null) {
              return true;
          }
          return Hypervisor.HypervisorType.XenServer.toString().equalsIgnoreCase(hypervisor);
      }
  
      @Override
      public Hypervisor.HypervisorType getHypervisorType() {
          return Hypervisor.HypervisorType.XenServer;
      }
  
      @Override
      public void postDiscovery(List<HostVO> hosts, long msId) throws DiscoveryException {
          //do nothing
      }
  
      @Override
      public int getTimeout() {
          return 0;
      }
  
      @Override
      public boolean isRecurring() {
          return false;
      }
  
      @Override
      public boolean processAnswers(long agentId, long seq, Answer[] answers) {
          return false;
      }
  
      @Override
      public boolean processCommands(long agentId, long seq, Command[] commands) {
          return false;
      }
  
      /**
       * Create the XenServer tools ISO entry in the database.
       * If there is already an entry with 'isoName' equals to {@value #xenServerIsoName} , we update its 'displayText' to {@value #xenServerIsoDisplayText}.
       * Otherwise, we create a new entry.
       */
      protected void createXenServerToolsIsoEntryInDatabase() {
          VMTemplateVO tmplt = _tmpltDao.findByTemplateName(xenServerIsoName);
          if (tmplt == null) {
              long id = _tmpltDao.getNextInSequence(Long.class, "id");
              VMTemplateVO template = VMTemplateVO.createPreHostIso(id, xenServerIsoName, xenServerIsoName, ImageFormat.ISO, true, true, TemplateType.PERHOST, null, null, true, 64,
                      Account.ACCOUNT_ID_SYSTEM, null, xenServerIsoDisplayText, false, 1, false, HypervisorType.XenServer);
              _tmpltDao.persist(template);
          } else {
              long id = tmplt.getId();
              tmplt.setTemplateType(TemplateType.PERHOST);
              tmplt.setUrl(null);
              tmplt.setDisplayText(xenServerIsoDisplayText);
              _tmpltDao.update(id, tmplt);
          }
      }
  
      @Override
      public void processHostAdded(long hostId) {
      }
  
      @Override
      public void processConnect(com.cloud.host.Host agent, StartupCommand cmd, boolean forRebalance) throws ConnectionException {
          if (!(cmd instanceof StartupRoutingCommand)) {
              return;
          }
          long agentId = agent.getId();
  
          StartupRoutingCommand startup = (StartupRoutingCommand)cmd;
          if (startup.getHypervisorType() != HypervisorType.XenServer) {
              logger.debug("Not XenServer so moving on.");
              return;
          }
  
          HostVO host = _hostDao.findById(agentId);
  
          ClusterVO cluster = _clusterDao.findById(host.getClusterId());
          if (cluster.getGuid() == null) {
              cluster.setGuid(startup.getPool());
              _clusterDao.update(cluster.getId(), cluster);
          } else if (!cluster.getGuid().equals(startup.getPool())) {
              String msg = "pool uuid for cluster " + cluster.getId() + " changed from " + cluster.getGuid() + " to " + startup.getPool();
              logger.warn(msg);
              throw new CloudRuntimeException(msg);
          }
  
          Map<String, String> details = startup.getHostDetails();
          String prodBrand = details.get("product_brand").trim();
          String prodVersion = details.get("product_version").trim();
          String hotfix = details.get(XenserverConfigs.XS620HotFix);
          String prodVersionTextShort = details.get("product_version_text_short");
  
          String resource = createServerResource(prodBrand, prodVersion, prodVersionTextShort, hotfix).getClass().getName();
  
          if (!resource.equals(host.getResource())) {
              String msg = "host " + host.getPrivateIpAddress() + " changed from " + host.getResource() + " to " + resource;
              logger.debug(msg);
              host.setResource(resource);
              host.setSetup(false);
              _hostDao.update(agentId, host);
              throw new HypervisorVersionChangedException(msg);
          }
  
          if (logger.isDebugEnabled()) {
              logger.debug("Setting up host " + agentId);
          }
          HostEnvironment env = new HostEnvironment();
  
          SetupCommand setup = new SetupCommand(env);
          if (_setupMultipath) {
              setup.setMultipathOn();
          }
          if (!host.isSetup()) {
              setup.setNeedSetup(true);
          }
  
          try {
              Answer answer = _agentMgr.send(agentId, setup);
              if (answer != null && answer.getResult() && answer instanceof SetupAnswer) {
                  host.setSetup(true);
                  host.setLastPinged((System.currentTimeMillis() >> 10) - 5 * 60);
                  host.setHypervisorVersion(prodVersion);
                  _hostDao.update(host.getId(), host);
                  if (((SetupAnswer)answer).needReconnect()) {
                      throw new ConnectionException(false, "Reinitialize agent after setup.");
                  }
                  return;
              } else {
                  logger.warn("Unable to setup agent " + agentId + " due to " + ((answer != null) ? answer.getDetails() : "return null"));
              }
          } catch (AgentUnavailableException e) {
              logger.warn("Unable to setup agent " + agentId + " because it became unavailable.", e);
          } catch (OperationTimedoutException e) {
              logger.warn("Unable to setup agent " + agentId + " because it timed out", e);
          }
          throw new ConnectionException(true, "Reinitialize agent after setup.");
      }
  
      @Override
      public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) {
          return null;
      }
  
      @Override
      public boolean processDisconnect(long agentId, Status state) {
          return false;
      }
  
      @Override
      public void processHostAboutToBeRemoved(long hostId) {
      }
  
      @Override
      public void processHostRemoved(long hostId, long clusterId) {
      }
  
      @Override
      public boolean processTimeout(long agentId, long seq) {
          return false;
      }
  
      @Override
      public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
          // TODO Auto-generated method stub
          return null;
      }
  
      @Override
      public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) {
          StartupCommand firstCmd = startup[0];
          if (!(firstCmd instanceof StartupRoutingCommand)) {
              return null;
          }
  
          StartupRoutingCommand ssCmd = ((StartupRoutingCommand)firstCmd);
          if (ssCmd.getHypervisorType() != HypervisorType.XenServer) {
              return null;
          }
  
          HostPodVO pod = _podDao.findById(host.getPodId());
          DataCenterVO dc = _dcDao.findById(host.getDataCenterId());
          logger.info("Host: " + host.getName() + " connected with hypervisor type: " + HypervisorType.XenServer + ". Checking CIDR...");
          _resourceMgr.checkCIDR(pod, dc, ssCmd.getPrivateIpAddress(), ssCmd.getPrivateNetmask());
          return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.XenServer, details, hostTags);
      }
  
      @Override
      public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
          if (host.getType() != com.cloud.host.Host.Type.Routing || host.getHypervisorType() != HypervisorType.XenServer) {
              return null;
          }
  
          _resourceMgr.deleteRoutingHost(host, isForced, isForceDeleteStorage);
          return new DeleteHostAnswer(true);
      }
  
      @Override
      protected HashMap<String, Object> buildConfigParams(HostVO host) {
          HashMap<String, Object> params = super.buildConfigParams(host);
          DataCenterVO zone = _dcDao.findById(host.getDataCenterId());
          if (zone != null) {
              boolean securityGroupEnabled = zone.isSecurityGroupEnabled() || _networkMgr.isSecurityGroupSupportedForZone(zone.getId());
              params.put("securitygroupenabled", Boolean.toString(securityGroupEnabled));
          }
          return params;
      }
  
      @Override
      public boolean stop() {
          _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName());
          return super.stop();
      }
  }
  