@Override
  public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
    StartupCommand firstCmd = cmd[0];
    if (!(firstCmd instanceof StartupRoutingCommand)) {
      return null;
    }

    StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd);
    if (ssCmd.getHypervisorType() != HypervisorType.KVM) {
      return null;
    }

    /* KVM requires host are the same in cluster */
    ClusterVO clusterVO = _clusterDao.findById(host.getClusterId());
    List<HostVO> hostsInCluster = _resourceMgr.listAllHostsInCluster(clusterVO.getId());
    if (!hostsInCluster.isEmpty()) {
      HostVO oneHost = hostsInCluster.get(0);
      _hostDao.loadDetails(oneHost);
      String hostOsInCluster = oneHost.getDetail("Host.OS");
      String hostOs = ssCmd.getHostDetails().get("Host.OS");
      if (!hostOsInCluster.equalsIgnoreCase(hostOs)) {
        throw new IllegalArgumentException(
            "Can't add host: "
                + firstCmd.getPrivateIpAddress()
                + " with hostOS: "
                + hostOs
                + " into a cluster,"
                + "in which there are "
                + hostOsInCluster
                + " hosts added");
      }
    }

    _hostDao.loadDetails(host);

    return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.KVM, host.getDetails(), null);
  }
  private List<SspClient> fetchSspClients(
      Long physicalNetworkId, Long dataCenterId, boolean enabled_only) {
    ArrayList<SspClient> clients = new ArrayList<SspClient>();

    boolean provider_found = false;
    PhysicalNetworkServiceProviderVO provider =
        _physicalNetworkServiceProviderDao.findByServiceProvider(physicalNetworkId, s_SSP_NAME);
    if (enabled_only) {
      if (provider != null && provider.getState() == State.Enabled) {
        provider_found = true;
      }
    } else {
      provider_found = true;
    }

    if (physicalNetworkId != null && provider_found) {
      SspCredentialVO credential = _sspCredentialDao.findByZone(dataCenterId);
      List<HostVO> hosts =
          _resourceMgr.listAllHostsInOneZoneByType(Host.Type.L2Networking, dataCenterId);
      for (HostVO host : hosts) {
        assert (credential != null);
        _hostDao.loadDetails(host);
        if ("v1Api".equals(host.getDetail("sspHost"))) {
          clients.add(
              new SspClient(
                  host.getDetail("url"), credential.getUsername(), credential.getPassword()));
        }
      }
    }
    if (clients.size() == 0) {
      String global_apiUrl = _configDao.getValueAndInitIfNotExist("ssp.url", "Network", null);
      String global_username =
          _configDao.getValueAndInitIfNotExist("ssp.username", "Network", null);
      String global_password =
          _configDao.getValueAndInitIfNotExist("ssp.password", "Network", null);
      if (global_apiUrl != null && global_username != null && global_password != null) {
        clients.add(new SspClient(global_apiUrl, global_username, global_password));
      }
    }
    return clients;
  }
  @Override
  public ServerResource reloadResource(HostVO host) {
    String resourceName = host.getResource();
    ServerResource resource = getResource(resourceName);

    if (resource != null) {
      _hostDao.loadDetails(host);

      HashMap<String, Object> params = buildConfigParams(host);
      try {
        resource.configure(host.getName(), params);
      } catch (ConfigurationException e) {
        s_logger.warn("Unable to configure resource due to " + e.getMessage());
        return null;
      }
      if (!resource.start()) {
        s_logger.warn("Unable to start the resource");
        return null;
      }
    }
    return resource;
  }
  @Override
  public Map<? extends ServerResource, Map<String, String>> find(
      long dcId,
      Long podId,
      Long clusterId,
      URI uri,
      String username,
      String password,
      List<String> hostTags)
      throws DiscoveryException {

    ClusterVO cluster = _clusterDao.findById(clusterId);
    if (cluster == null || cluster.getHypervisorType() != HypervisorType.KVM) {
      if (s_logger.isInfoEnabled())
        s_logger.info("invalid cluster id or cluster is not for KVM hypervisors");
      return null;
    }

    Map<KvmDummyResourceBase, Map<String, String>> resources =
        new HashMap<KvmDummyResourceBase, Map<String, String>>();
    Map<String, String> details = new HashMap<String, String>();
    if (!uri.getScheme().equals("http")) {
      String msg =
          "urlString is not http so we're not taking care of the discovery for this: " + uri;
      s_logger.debug(msg);
      return null;
    }
    com.trilead.ssh2.Connection sshConnection = null;
    String agentIp = null;
    try {

      String hostname = uri.getHost();
      InetAddress ia = InetAddress.getByName(hostname);
      agentIp = ia.getHostAddress();
      String guid = UUID.nameUUIDFromBytes(agentIp.getBytes()).toString();
      String guidWithTail = guid + "-LibvirtComputingResource"; /*tail added by agent.java*/
      if (_resourceMgr.findHostByGuid(guidWithTail) != null) {
        s_logger.debug(
            "Skipping " + agentIp + " because " + guidWithTail + " is already in the database.");
        return null;
      }

      sshConnection = new com.trilead.ssh2.Connection(agentIp, 22);

      sshConnection.connect(null, 60000, 60000);
      if (!sshConnection.authenticateWithPassword(username, password)) {
        s_logger.debug("Failed to authenticate");
        throw new DiscoveredWithErrorException("Authentication error");
      }

      if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "lsmod|grep kvm", 3)) {
        s_logger.debug("It's not a KVM enabled machine");
        return null;
      }

      List<PhysicalNetworkSetupInfo> netInfos =
          _networkMgr.getPhysicalNetworkInfo(dcId, HypervisorType.KVM);
      String kvmPrivateNic = _kvmPrivateNic;
      String kvmPublicNic = _kvmPublicNic;
      String kvmGuestNic = _kvmGuestNic;

      for (PhysicalNetworkSetupInfo info : netInfos) {
        if (info.getPrivateNetworkName() != null) {
          kvmPrivateNic = info.getPrivateNetworkName();
        }
        if (info.getPublicNetworkName() != null) {
          kvmPublicNic = info.getPublicNetworkName();
        }
        if (info.getGuestNetworkName() != null) {
          kvmGuestNic = info.getGuestNetworkName();
        }
      }

      String parameters =
          " -m " + _hostIp + " -z " + dcId + " -p " + podId + " -c " + clusterId + " -g " + guid
              + " -a";

      if (kvmPublicNic != null) {
        parameters += " --pubNic=" + kvmPublicNic;
      }

      if (kvmPrivateNic != null) {
        parameters += " --prvNic=" + kvmPrivateNic;
      }

      if (kvmGuestNic != null) {
        parameters += " --guestNic=" + kvmGuestNic;
      }

      SSHCmdHelper.sshExecuteCmd(sshConnection, "cloud-setup-agent " + parameters, 3);

      KvmDummyResourceBase kvmResource = new KvmDummyResourceBase();
      Map<String, Object> params = new HashMap<String, Object>();

      params.put("zone", Long.toString(dcId));
      params.put("pod", Long.toString(podId));
      params.put("cluster", Long.toString(clusterId));
      params.put("guid", guid);
      params.put("agentIp", agentIp);
      kvmResource.configure("kvm agent", params);
      resources.put(kvmResource, details);

      HostVO connectedHost = waitForHostConnect(dcId, podId, clusterId, guidWithTail);
      if (connectedHost == null) return null;

      details.put("guid", guidWithTail);

      // place a place holder guid derived from cluster ID
      if (cluster.getGuid() == null) {
        cluster.setGuid(UUID.nameUUIDFromBytes(String.valueOf(clusterId).getBytes()).toString());
        _clusterDao.update(clusterId, cluster);
      }

      // save user name and password
      _hostDao.loadDetails(connectedHost);
      Map<String, String> hostDetails = connectedHost.getDetails();
      hostDetails.put("password", password);
      hostDetails.put("username", username);
      _hostDao.saveDetails(connectedHost);
      return resources;
    } catch (DiscoveredWithErrorException e) {
      throw e;
    } catch (Exception e) {
      String msg = " can't setup agent, due to " + e.toString() + " - " + e.getMessage();
      s_logger.warn(msg);
    } finally {
      if (sshConnection != null) sshConnection.close();
    }

    return null;
  }
  @Override
  public Network implement(
      final Network network,
      final NetworkOffering offering,
      final DeployDestination dest,
      final ReservationContext context)
      throws InsufficientVirtualNetworkCapacityException {
    assert network.getState() == State.Implementing : "Why are we implementing " + network;

    final long dcId = dest.getDataCenter().getId();

    Long physicalNetworkId = network.getPhysicalNetworkId();

    // physical network id can be null in Guest Network in Basic zone, so locate the physical
    // network
    if (physicalNetworkId == null) {
      physicalNetworkId =
          networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType());
    }

    final NetworkVO implemented =
        new NetworkVO(
            network.getTrafficType(),
            network.getMode(),
            network.getBroadcastDomainType(),
            network.getNetworkOfferingId(),
            State.Allocated,
            network.getDataCenterId(),
            physicalNetworkId,
            offering.getRedundantRouter());

    if (network.getGateway() != null) {
      implemented.setGateway(network.getGateway());
    }

    if (network.getCidr() != null) {
      implemented.setCidr(network.getCidr());
    }

    // Name is either the given name or the uuid
    String name = network.getName();
    if (name == null || name.isEmpty()) {
      name = ((NetworkVO) network).getUuid();
    }
    if (name.length() > MAX_NAME_LENGTH) {
      name = name.substring(0, MAX_NAME_LENGTH - 1);
    }

    final List<NiciraNvpDeviceVO> devices = niciraNvpDao.listByPhysicalNetwork(physicalNetworkId);
    if (devices.isEmpty()) {
      s_logger.error("No NiciraNvp Controller on physical network " + physicalNetworkId);
      return null;
    }
    final NiciraNvpDeviceVO niciraNvpDevice = devices.get(0);
    final HostVO niciraNvpHost = hostDao.findById(niciraNvpDevice.getHostId());
    hostDao.loadDetails(niciraNvpHost);
    final String transportzoneuuid = niciraNvpHost.getDetail("transportzoneuuid");
    final String transportzoneisotype = niciraNvpHost.getDetail("transportzoneisotype");

    final CreateLogicalSwitchCommand cmd =
        new CreateLogicalSwitchCommand(
            transportzoneuuid,
            transportzoneisotype,
            name,
            context.getDomain().getName() + "-" + context.getAccount().getAccountName());
    final CreateLogicalSwitchAnswer answer =
        (CreateLogicalSwitchAnswer) agentMgr.easySend(niciraNvpHost.getId(), cmd);

    if (answer == null || !answer.getResult()) {
      s_logger.error("CreateLogicalSwitchCommand failed");
      return null;
    }

    try {
      implemented.setBroadcastUri(new URI("lswitch", answer.getLogicalSwitchUuid(), null));
      implemented.setBroadcastDomainType(BroadcastDomainType.Lswitch);
      s_logger.info(
          "Implemented OK, network linked to  = " + implemented.getBroadcastUri().toString());
    } catch (final URISyntaxException e) {
      s_logger.error(
          "Unable to store logical switch id in broadcast uri, uuid = " + implemented.getUuid(), e);
      return null;
    }

    return implemented;
  }
  @Override
  public Host addSspHost(AddSspCmd cmd) {
    SspClient client = new SspClient(cmd.getUrl(), cmd.getUsername(), cmd.getPassword());
    if (!client.login()) {
      throw new CloudRuntimeException("Ssp login failed.");
    }

    long zoneId = cmd.getZoneId();
    SspCredentialVO credential = _sspCredentialDao.findByZone(zoneId);
    if (credential == null) {
      if (cmd.getUsername() == null || cmd.getPassword() == null) {
        throw new InvalidParameterValueException("Initial credential required for zone: " + zoneId);
      }
      credential = new SspCredentialVO();
      credential.setZoneId(zoneId);
      credential.setUsername(cmd.getUsername());
      credential.setPassword(cmd.getPassword());
      _sspCredentialDao.persist(credential);
    } else {
      if (cmd.getUsername() != null || cmd.getPassword() != null) {
        s_logger.warn("Tenant credential already configured for zone:" + zoneId);
      }
    }

    String tenantUuid = _sspTenantDao.findUuidByZone(zoneId);
    if (tenantUuid == null) {
      if (cmd.getTenantUuid() == null) {
        throw new InvalidParameterValueException(
            "Initial tenant uuid required for zone: " + zoneId);
      }
      SspTenantVO tenant = new SspTenantVO();
      tenant.setZoneId(zoneId);
      tenant.setUuid(cmd.getTenantUuid());
      _sspTenantDao.persist(tenant);
    } else {
      if (cmd.getTenantUuid() != null) {
        s_logger.warn("Tenant uuid already configured for zone:" + zoneId);
      }
    }

    String normalizedUrl = null;
    String hostname = null;
    try {
      URL url = new URL(cmd.getUrl());
      normalizedUrl = url.toString();
      hostname = url.getHost();
    } catch (MalformedURLException e1) {
      throw new CloudRuntimeException("Invalid url " + cmd.getUrl());
    }

    List<HostVO> hosts = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.L2Networking, zoneId);
    for (HostVO host : hosts) {
      assert (credential != null);
      _hostDao.loadDetails(host);
      if ("v1Api".equals(host.getDetail("sspHost"))) {
        if (normalizedUrl.equals(host.getDetail("url"))) {
          s_logger.warn("Ssp host already registered " + normalizedUrl);
          return host;
        }
      }
    }
    // SspHost HostVO will be created per zone and url.
    HostVO host = new HostVO(UUID.randomUUID().toString());
    host.setDataCenterId(zoneId);
    host.setType(Host.Type.L2Networking);
    host.setPrivateIpAddress(hostname); // db schema not null. It may be a name, not IP address.
    //        host.setPrivateMacAddress(""); // db schema nullable
    //        host.setPrivateNetmask(""); // db schema nullable
    host.setVersion("1"); // strange db schema not null
    host.setName(cmd.getName());

    host.setDetails(new HashMap<String, String>());
    host.setDetail("sspHost", "v1Api");
    host.setDetail("url", normalizedUrl);
    return _hostDao.persist(host);
  }
  @Override
  public boolean implement(
      Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context)
      throws ConcurrentOperationException, ResourceUnavailableException,
          InsufficientCapacityException {
    DataCenter zone = _configMgr.getZone(network.getDataCenterId());

    if (zone.getNetworkType() == NetworkType.Basic) {
      s_logger.debug("Not handling network implement in zone of type " + NetworkType.Basic);
      return false;
    }

    if (!canHandle(network)) {
      return false;
    }

    List<CiscoVnmcControllerVO> devices =
        _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId());
    if (devices.isEmpty()) {
      s_logger.error("No Cisco Vnmc device on network " + network.getName());
      return false;
    }

    List<CiscoAsa1000vDeviceVO> asaList =
        _ciscoAsa1000vDao.listByPhysicalNetwork(network.getPhysicalNetworkId());
    if (asaList.isEmpty()) {
      s_logger.debug("No Cisco ASA 1000v device on network " + network.getName());
      return false;
    }

    NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId());
    if (asaForNetwork != null) {
      s_logger.debug("Cisco ASA 1000v device already associated with network " + network.getName());
      return true;
    }

    if (!_networkModel.isProviderSupportServiceInNetwork(
        network.getId(), Service.SourceNat, Provider.CiscoVnmc)) {
      s_logger.error(
          "SourceNat service is not provided by Cisco Vnmc device on network " + network.getName());
      return false;
    }

    Transaction txn = Transaction.currentTxn();
    boolean status = false;
    try {
      txn.start();

      // ensure that there is an ASA 1000v assigned to this network
      CiscoAsa1000vDevice assignedAsa = assignAsa1000vToNetwork(network);
      if (assignedAsa == null) {
        s_logger.error("Unable to assign ASA 1000v device to network " + network.getName());
        return false;
      }

      ClusterVO asaCluster = _clusterDao.findById(assignedAsa.getClusterId());
      ClusterVSMMapVO clusterVsmMap = _clusterVsmMapDao.findByClusterId(assignedAsa.getClusterId());
      if (clusterVsmMap == null) {
        s_logger.error(
            "Vmware cluster "
                + asaCluster.getName()
                + " has no Cisco Nexus VSM device associated with it");
        return false;
      }

      CiscoNexusVSMDeviceVO vsmDevice = _vsmDeviceDao.findById(clusterVsmMap.getVsmId());
      if (vsmDevice == null) {
        s_logger.error(
            "Unable to load details of Cisco Nexus VSM device associated with cluster "
                + asaCluster.getName());
        return false;
      }

      CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0);
      HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId());
      _hostDao.loadDetails(ciscoVnmcHost);
      Account owner = context.getAccount();
      PublicIp sourceNatIp = _ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network);
      String vlan = network.getBroadcastUri().getHost();
      long vlanId = Long.parseLong(vlan);

      List<VlanVO> vlanVOList =
          _vlanDao.listVlansByPhysicalNetworkId(network.getPhysicalNetworkId());
      List<String> publicGateways = new ArrayList<String>();
      for (VlanVO vlanVO : vlanVOList) {
        publicGateways.add(vlanVO.getVlanGateway());
      }

      // due to VNMC limitation of not allowing source NAT ip as the outside ip of firewall,
      // an additional public ip needs to acquired for assigning as firewall outside ip.
      // In case there are already additional ip addresses available (network restart) use one
      // of them such that it is not the source NAT ip
      IpAddress outsideIp = null;
      List<IPAddressVO> publicIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
      for (IPAddressVO ip : publicIps) {
        if (!ip.isSourceNat()) {
          outsideIp = ip;
          break;
        }
      }
      if (outsideIp == null) { // none available, acquire one
        try {
          Account caller = CallContext.current().getCallingAccount();
          long callerUserId = CallContext.current().getCallingUserId();
          outsideIp = _ipAddrMgr.allocateIp(owner, false, caller, callerUserId, zone);
        } catch (ResourceAllocationException e) {
          s_logger.error("Unable to allocate additional public Ip address. Exception details " + e);
          return false;
        }

        try {
          outsideIp =
              _ipAddrMgr.associateIPToGuestNetwork(outsideIp.getId(), network.getId(), true);
        } catch (ResourceAllocationException e) {
          s_logger.error(
              "Unable to assign allocated additional public Ip "
                  + outsideIp.getAddress().addr()
                  + " to network with vlan "
                  + vlanId
                  + ". Exception details "
                  + e);
          return false;
        }
      }

      // create logical edge firewall in VNMC
      String gatewayNetmask = NetUtils.getCidrNetmask(network.getCidr());
      // due to ASA limitation of allowing single subnet to be assigned to firewall interfaces,
      // all public ip addresses must be from same subnet, this essentially means single public
      // subnet in zone
      if (!createLogicalEdgeFirewall(
          vlanId,
          network.getGateway(),
          gatewayNetmask,
          outsideIp.getAddress().addr(),
          sourceNatIp.getNetmask(),
          publicGateways,
          ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to create logical edge firewall in Cisco VNMC device for network "
                + network.getName());
        return false;
      }

      // create stuff in VSM for ASA device
      if (!configureNexusVsmForAsa(
          vlanId,
          network.getGateway(),
          vsmDevice.getUserName(),
          vsmDevice.getPassword(),
          vsmDevice.getipaddr(),
          assignedAsa.getInPortProfile(),
          ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to configure Cisco Nexus VSM "
                + vsmDevice.getipaddr()
                + " for ASA device for network "
                + network.getName());
        return false;
      }

      // configure source NAT
      if (!configureSourceNat(vlanId, network.getCidr(), sourceNatIp, ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to configure source NAT in Cisco VNMC device for network " + network.getName());
        return false;
      }

      // associate Asa 1000v instance with logical edge firewall
      if (!associateAsaWithLogicalEdgeFirewall(
          vlanId, assignedAsa.getManagementIp(), ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to associate Cisco ASA 1000v ("
                + assignedAsa.getManagementIp()
                + ") with logical edge firewall in VNMC for network "
                + network.getName());
        return false;
      }

      status = true;
      txn.commit();
    } finally {
      if (!status) {
        txn.rollback();
        // FIXME: also undo changes in VNMC, VSM if anything failed
      }
    }

    return true;
  }