@Override
  @DB
  public boolean prepare(
      Network network,
      NicProfile nic,
      VirtualMachineProfile<? extends VirtualMachine> vm,
      DeployDestination dest,
      ReservationContext context)
      throws ConcurrentOperationException, ResourceUnavailableException,
          InsufficientCapacityException {
    Host host = dest.getHost();
    if (host == null || host.getHypervisorType() != HypervisorType.BareMetal) {
      return true;
    }

    Transaction txn = Transaction.currentTxn();
    txn.start();
    nic.setMacAddress(host.getPrivateMacAddress());
    NicVO vo = _nicDao.findById(nic.getId());
    assert vo != null : "Where ths nic " + nic.getId() + " going???";
    vo.setMacAddress(nic.getMacAddress());
    _nicDao.update(vo.getId(), vo);
    txn.commit();
    s_logger.debug(
        "Bare Metal changes mac address of nic " + nic.getId() + " to " + nic.getMacAddress());

    return _dhcpMgr.addVirtualMachineIntoNetwork(network, nic, vm, dest, context);
  }
  @Override
  public void reserve(
      NicProfile nic,
      Network config,
      VirtualMachineProfile<? extends VirtualMachine> vm,
      DeployDestination dest,
      ReservationContext context)
      throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException {
    assert nic.getTrafficType() == TrafficType.Control;

    if (dest.getHost().getHypervisorType() == HypervisorType.VmWare
        && vm.getType() == VirtualMachine.Type.DomainRouter) {
      super.reserve(nic, config, vm, dest, context);

      String mac = _networkMgr.getNextAvailableMacAddressInNetwork(config.getId());
      nic.setMacAddress(mac);
      return;
    }

    String ip =
        _dcDao.allocateLinkLocalIpAddress(
            dest.getDataCenter().getId(),
            dest.getPod().getId(),
            nic.getId(),
            context.getReservationId());
    nic.setIp4Address(ip);
    nic.setMacAddress(NetUtils.long2Mac(NetUtils.ip2Long(ip) | (14l << 40)));
    nic.setNetmask("255.255.0.0");
    nic.setFormat(AddressFormat.Ip4);
    nic.setGateway(NetUtils.getLinkLocalGateway());
  }
  // we use context.reservationId for dedup of guru & element operations.
  public boolean createNicEnv(
      Network network, NicProfile nic, DeployDestination dest, ReservationContext context) {
    String tenantNetworkUuid = _sspUuidDao.findUuidByNetwork(network);
    if (tenantNetworkUuid == null) {
      s_logger.debug("Skipping #createNicEnv() for nic on " + network.toString());
      return true;
    }

    String reservationId = context.getReservationId();
    List<SspUuidVO> tenantPortUuidVos = _sspUuidDao.listUUidVoByNicProfile(nic);
    for (SspUuidVO tenantPortUuidVo : tenantPortUuidVos) {
      if (reservationId.equals(tenantPortUuidVo.getReservationId())) {
        s_logger.info("Skipping because reservation found " + reservationId);
        return true;
      }
    }

    String tenantPortUuid = null;
    for (SspClient client :
        fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
      SspClient.TenantPort sspPort = client.createTenantPort(tenantNetworkUuid);
      if (sspPort != null) {
        tenantPortUuid = sspPort.uuid;
        nic.setReservationId(reservationId);

        SspUuidVO uuid = new SspUuidVO();
        uuid.setUuid(tenantPortUuid);
        uuid.setObjClass(SspUuidVO.objClassNicProfile);
        uuid.setObjId(nic.getId());
        uuid.setReservationId(reservationId);
        _sspUuidDao.persist(uuid);
        break;
      }
    }
    if (tenantPortUuid == null) {
      s_logger.debug("#createNicEnv() failed for nic on " + network.toString());
      return false;
    }

    for (SspClient client :
        fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
      SspClient.TenantPort sspPort =
          client.updateTenantVifBinding(tenantPortUuid, dest.getHost().getPrivateIpAddress());
      if (sspPort != null) {
        if (sspPort.vlanId != null) {
          nic.setBroadcastType(BroadcastDomainType.Vlan);
          nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(String.valueOf(sspPort.vlanId)));
        }
        return true;
      }
    }
    s_logger.error("Updating vif failed " + nic.toString());
    return false;
  }
  public boolean deleteNicEnv(Network network, NicProfile nic, ReservationContext context) {
    if (context == null) {
      s_logger.error("ReservationContext was null for " + nic + " " + network);
      return false;
    }
    String reservationId = context.getReservationId();

    SspUuidVO deleteTarget = null;
    SspUuidVO remainingTarget = null;
    List<SspUuidVO> tenantPortUuidVos = _sspUuidDao.listUUidVoByNicProfile(nic);
    for (SspUuidVO tenantPortUuidVo : tenantPortUuidVos) {
      if (reservationId.equals(tenantPortUuidVo.getReservationId())) {
        deleteTarget = tenantPortUuidVo;
      } else {
        remainingTarget = tenantPortUuidVo;
      }
    }

    if (deleteTarget != null) { // delete the target ssp uuid (tenant-port)
      String tenantPortUuid = deleteTarget.getUuid();
      boolean processed = false;
      for (SspClient client :
          fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
        SspClient.TenantPort sspPort = client.updateTenantVifBinding(tenantPortUuid, null);
        if (sspPort != null) {
          processed = true;
          break;
        }
      }
      if (!processed) {
        s_logger.warn("Ssp api nic detach failed " + nic.toString());
      }
      processed = false;
      for (SspClient client :
          fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
        if (client.deleteTenantPort(tenantPortUuid)) {
          _sspUuidDao.removeUuid(tenantPortUuid);
          processed = true;
          break;
        }
      }
      if (!processed) {
        s_logger.warn("Ssp api tenant port deletion failed " + nic.toString());
      }
      _sspUuidDao.removeUuid(tenantPortUuid);
    }
    if (remainingTarget != null) {
      NicVO nicVo = _nicDao.findById(nic.getId());
      nicVo.setReservationId(remainingTarget.getReservationId());
      _nicDao.persist(nicVo); // persist the new reservationId
    }
    return true;
  }
  @Override
  public boolean release(
      NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm, String reservationId) {
    assert nic.getTrafficType() == TrafficType.Control;

    if (vm.getHypervisorType() == HypervisorType.VmWare
        && vm.getType() == VirtualMachine.Type.DomainRouter) {
      super.release(nic, vm, reservationId);
      return true;
    }

    _dcDao.releaseLinkLocalIpAddress(nic.getId(), reservationId);
    nic.setIp4Address(null);
    nic.setMacAddress(null);
    nic.setNetmask(null);
    nic.setFormat(null);
    nic.setGateway(null);

    return true;
  }
  @Override
  public boolean addUserData(NicProfile nic, VirtualMachineProfile profile) {
    UserVmVO vm = _vmDao.findById(profile.getVirtualMachine().getId());
    _vmDao.loadDetails(vm);

    String serviceOffering =
        _serviceOfferingDao
            .findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId())
            .getDisplayText();
    String zoneName = _dcDao.findById(vm.getDataCenterId()).getName();
    NicVO nvo = _nicDao.findById(nic.getId());
    VmDataCommand cmd =
        new VmDataCommand(
            nvo.getIPv4Address(), vm.getInstanceName(), _ntwkModel.getExecuteInSeqNtwkElmtCmd());
    // if you add new metadata files, also edit
    // systemvm/patches/debian/config/var/www/html/latest/.htaccess
    cmd.addVmData("userdata", "user-data", vm.getUserData());
    cmd.addVmData("metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering));
    cmd.addVmData("metadata", "availability-zone", StringUtils.unicodeEscape(zoneName));
    cmd.addVmData("metadata", "local-ipv4", nic.getIPv4Address());
    cmd.addVmData("metadata", "local-hostname", StringUtils.unicodeEscape(vm.getInstanceName()));
    cmd.addVmData("metadata", "public-ipv4", nic.getIPv4Address());
    cmd.addVmData("metadata", "public-hostname", StringUtils.unicodeEscape(vm.getInstanceName()));
    cmd.addVmData("metadata", "instance-id", String.valueOf(vm.getId()));
    cmd.addVmData("metadata", "vm-id", String.valueOf(vm.getInstanceName()));
    cmd.addVmData("metadata", "public-keys", null);
    String cloudIdentifier = _configDao.getValue("cloud.identifier");
    if (cloudIdentifier == null) {
      cloudIdentifier = "";
    } else {
      cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}";
    }
    cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier);

    List<PhysicalNetworkVO> phys = _phynwDao.listByZone(vm.getDataCenterId());
    if (phys.isEmpty()) {
      throw new CloudRuntimeException(
          String.format("Cannot find physical network in zone %s", vm.getDataCenterId()));
    }
    if (phys.size() > 1) {
      throw new CloudRuntimeException(
          String.format(
              "Baremetal only supports one physical network in zone, but zone %s has %s physical networks",
              vm.getDataCenterId(), phys.size()));
    }
    PhysicalNetworkVO phy = phys.get(0);

    QueryBuilder<BaremetalPxeVO> sc = QueryBuilder.create(BaremetalPxeVO.class);
    // TODO: handle both kickstart and PING
    // sc.addAnd(sc.getEntity().getPodId(), Op.EQ, vm.getPodIdToDeployIn());
    sc.and(sc.entity().getPhysicalNetworkId(), Op.EQ, phy.getId());
    BaremetalPxeVO pxeVo = sc.find();
    if (pxeVo == null) {
      throw new CloudRuntimeException(
          "No PXE server found in pod: "
              + vm.getPodIdToDeployIn()
              + ", you need to add it before starting VM");
    }

    try {
      Answer ans = _agentMgr.send(pxeVo.getHostId(), cmd);
      if (!ans.getResult()) {
        s_logger.debug(
            String.format(
                "Add userdata to vm:%s failed because %s", vm.getInstanceName(), ans.getDetails()));
        return false;
      } else {
        return true;
      }
    } catch (Exception e) {
      s_logger.debug(String.format("Add userdata to vm:%s failed", vm.getInstanceName()), e);
      return false;
    }
  }