@Override
    public boolean apply(ServerRef serverRef) {
      checkNotNull(serverRef, "serverRef");
      // give time for the operation to actually start
      Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
      Server server = api.serverApi().getServer(serverRef.dataCenterId(), serverRef.serverId());

      if (server == null || server.properties().vmState() == null) {
        return false;
      }

      return server.properties().vmState() == expectedStatus;
    }
  @Test
  public void testGetServerWithDepth() throws InterruptedException {
    MockResponse response = new MockResponse();
    response.setBody(stringFromResource("/server/get-depth-5.json"));
    response.setHeader("Content-Type", "application/vnd.profitbricks.resource+json");

    server.enqueue(response);

    Server server = serverApi().getServer("datacenter-id", "some-id", new DepthOptions().depth(5));

    assertNotNull(server);
    assertEquals(server.properties().name(), "kube-lb");

    assertEquals(this.server.getRequestCount(), 1);
    assertSent(this.server, "GET", "/datacenters/datacenter-id/servers/some-id?depth=5");
  }
  @Override
  public ServerInDataCenter getNode(String id) {
    DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(id);
    logger.trace("<< searching for server with id=%s", id);

    Server server =
        api.serverApi()
            .getServer(
                datacenterAndId.getDataCenter(),
                datacenterAndId.getId(),
                new DepthOptions().depth(3));
    if (server != null) {
      logger.trace(">> found server [%s]", server.properties().name());
    }
    return server == null ? null : new ServerInDataCenter(server, datacenterAndId.getDataCenter());
  }
  @Override
  public void destroyNode(String nodeId) {
    DataCenterAndId datacenterAndId = DataCenterAndId.fromSlashEncoded(nodeId);
    ServerApi serverApi = api.serverApi();
    Server server =
        serverApi.getServer(
            datacenterAndId.getDataCenter(), datacenterAndId.getId(), new DepthOptions().depth(5));
    if (server != null) {
      for (org.apache.jclouds.profitbricks.rest.domain.Volume volume :
          server.entities().volumes().items()) {
        destroyVolume(volume.id(), datacenterAndId.getDataCenter());
      }

      try {
        destroyServer(datacenterAndId.getId(), datacenterAndId.getDataCenter());
      } catch (Exception ex) {
        logger.warn(ex, ">> failed to delete server with id=%s", datacenterAndId.getId());
      }
    }
  }
  @Test
  public void testCreate() throws InterruptedException {
    server.enqueue(new MockResponse().setBody(stringFromResource("/server/get.json")));

    Server server =
        serverApi()
            .createServer(
                Server.Request.creatingBuilder()
                    .dataCenterId("datacenter-id")
                    .name("jclouds-node")
                    .cores(1)
                    .ram(1024)
                    .build());

    assertNotNull(server);
    assertNotNull(server.id());

    assertEquals(this.server.getRequestCount(), 1);
    assertSent(
        this.server,
        "POST",
        "/datacenters/datacenter-id/servers",
        "{\"properties\": {\"name\": \"jclouds-node\", \"cores\": 1, \"ram\": 1024}}");
  }
  protected NodeAndInitialCredentials<ServerInDataCenter> createNodeWithGroupEncodedIntoName(
      String group, String name, TemplateWithDataCenter template) {
    checkArgument(
        template.getLocation().getScope() == LocationScope.ZONE,
        "Template must use a ZONE-scoped location");
    final String dataCenterId = template.getDataCenter().id();
    Hardware hardware = template.getHardware();
    TemplateOptions options = template.getOptions();
    final String loginUser =
        isNullOrEmpty(options.getLoginUser()) ? "root" : options.getLoginUser();
    final String password =
        options.hasLoginPassword() ? options.getLoginPassword() : Passwords.generate();
    final org.jclouds.compute.domain.Image image = template.getImage();

    // provision all volumes based on hardware
    List<? extends Volume> volumes = hardware.getVolumes();
    List<String> volumeIds = Lists.newArrayListWithExpectedSize(volumes.size());

    int i = 1;
    for (final Volume volume : volumes) {
      try {
        logger.trace("<< provisioning volume '%s'", volume);
        final org.apache.jclouds.profitbricks.rest.domain.Volume.Request.CreatePayload.Builder
            request = org.apache.jclouds.profitbricks.rest.domain.Volume.Request.creatingBuilder();
        if (i == 1) {
          request.image(image.getId());
          // we don't need to pass password to the API if we're using a snapshot
          Provisionable.Type provisionableType =
              Provisionable.Type.fromValue(
                  image.getUserMetadata().get(ProvisionableToImage.KEY_PROVISIONABLE_TYPE));
          if (provisionableType == Provisionable.Type.IMAGE) {
            request.imagePassword(password);
          }
        }
        request
            .dataCenterId(dataCenterId)
            .name(format("%s-disk-%d", name, i++))
            .size(volume.getSize().intValue())
            .type(VolumeType.HDD);

        org.apache.jclouds.profitbricks.rest.domain.Volume vol =
            (org.apache.jclouds.profitbricks.rest.domain.Volume)
                provisioningManager.provision(
                    jobFactory.create(
                        dataCenterId,
                        new Supplier<Object>() {
                          @Override
                          public Object get() {
                            return api.volumeApi().createVolume(request.build());
                          }
                        }));

        volumeIds.add(vol.id());
        logger.trace(">> provisioning complete for volume. returned id='%s'", vol.id());
      } catch (Exception ex) {
        if (i - 1 == 1) // if first volume (one with image) provisioning fails; stop method
        {
          throw Throwables.propagate(ex);
        }
        logger.warn(ex, ">> failed to provision volume. skipping..");
      }
    }

    String volumeBootDeviceId = Iterables.get(volumeIds, 0); // must have atleast 1
    waitVolumeUntilAvailable.apply(VolumeRef.create(dataCenterId, volumeBootDeviceId));

    // provision server
    final Server server;
    Double cores = ComputeServiceUtils.getCores(hardware);

    Server.BootVolume bootVolume = Server.BootVolume.create(volumeBootDeviceId);

    try {
      final Server.Request.CreatePayload serverRequest =
          Server.Request.creatingBuilder()
              .dataCenterId(dataCenterId)
              .name(name)
              .bootVolume(bootVolume)
              .cores(cores.intValue())
              .ram(hardware.getRam())
              .build();

      logger.trace("<< provisioning server '%s'", serverRequest);

      server =
          (Server)
              provisioningManager.provision(
                  jobFactory.create(
                      dataCenterId,
                      new Supplier<Object>() {

                        @Override
                        public Object get() {
                          return api.serverApi().createServer(serverRequest);
                        }
                      }));
      logger.trace(">> provisioning complete for server. returned id='%s'", server.id());

    } catch (Exception ex) {
      logger.error(ex, ">> failed to provision server. rollbacking..");
      destroyVolumes(volumeIds, dataCenterId);
      throw Throwables.propagate(ex);
    }

    waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, server.id()));
    waitDcUntilAvailable.apply(dataCenterId);

    // attach bootVolume to Server
    org.apache.jclouds.profitbricks.rest.domain.Volume volume =
        api.serverApi()
            .attachVolume(
                Server.Request.attachVolumeBuilder()
                    .dataCenterId(dataCenterId)
                    .serverId(server.id())
                    .volumeId(bootVolume.id())
                    .build());

    trackables.waitUntilRequestCompleted(volume);
    waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, server.id()));
    waitDcUntilAvailable.apply(dataCenterId);

    // fetch an existing lan and creat if non was found
    Lan lan = null;

    List<Lan> lans = api.lanApi().list(dataCenterId);
    if (lans != null && !lans.isEmpty()) {
      lan =
          FluentIterable.from(lans)
              .firstMatch(
                  new Predicate<Lan>() {
                    @Override
                    public boolean apply(Lan input) {
                      input =
                          api.lanApi().get(dataCenterId, input.id(), new DepthOptions().depth(3));
                      return input.properties().isPublic();
                    }
                  })
              .orNull();
    }
    if (lan == null) {
      logger.warn("Could not find an existing lan Creating one....");
      lan =
          api.lanApi()
              .create(
                  Lan.Request.creatingBuilder()
                      .dataCenterId(dataCenterId)
                      .isPublic(Boolean.TRUE)
                      .name("lan " + name)
                      .build());
      trackables.waitUntilRequestCompleted(lan);
    }

    // add a NIC to the server
    int lanId = DEFAULT_LAN_ID;
    if (options.getNetworks() != null) {
      try {
        String networkId = Iterables.get(options.getNetworks(), 0);
        lanId = Integer.valueOf(networkId);
      } catch (Exception ex) {
        logger.warn(
            "no valid network id found from options. using default id='%d'", DEFAULT_LAN_ID);
      }
    }

    Nic nic =
        api.nicApi()
            .create(
                Nic.Request.creatingBuilder()
                    .dataCenterId(dataCenterId)
                    .name("jclouds" + name)
                    .dhcp(Boolean.TRUE)
                    .lan(lanId)
                    .firewallActive(Boolean.FALSE)
                    .serverId(server.id())
                    .build());

    trackables.waitUntilRequestCompleted(nic);
    waitNICUntilAvailable.apply(NicRef.create(dataCenterId, server.id(), nic.id()));
    waitDcUntilAvailable.apply(dataCenterId);
    waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, server.id()));

    // connect the rest of volumes to server;delete if fails
    final int volumeCount = volumeIds.size();
    for (int j = 1; j < volumeCount; j++) { // skip first; already connected
      final String volumeId = volumeIds.get(j);
      try {
        logger.trace("<< connecting volume '%s' to server '%s'", volumeId, server.id());
        provisioningManager.provision(
            jobFactory.create(
                group,
                new Supplier<Object>() {

                  @Override
                  public Object get() {
                    return api.serverApi()
                        .attachVolume(
                            Server.Request.attachVolumeBuilder()
                                .dataCenterId(dataCenterId)
                                .serverId(server.id())
                                .volumeId(volumeId)
                                .build());
                  }
                }));

        logger.trace(">> volume connected.");
      } catch (Exception ex) {
        try {
          // delete unconnected volume
          logger.warn(ex, ">> failed to connect volume '%s'. deleting..", volumeId);
          destroyVolume(volumeId, dataCenterId);
          logger.warn(ex, ">> rolling back server..", server.id());
          destroyServer(server.id(), dataCenterId);
          throw ex;
        } catch (Exception ex1) {
          logger.error(ex, ">> failed to rollback");
        }
      }
    }
    waitDcUntilAvailable.apply(dataCenterId);
    waitServerUntilAvailable.apply(ServerRef.create(dataCenterId, server.id()));

    LoginCredentials serverCredentials =
        LoginCredentials.builder().user(loginUser).password(password).build();

    String serverInDataCenterId =
        DataCenterAndId.fromDataCenterAndId(dataCenterId, server.id()).slashEncode();
    ServerInDataCenter serverInDatacenter = getNode(serverInDataCenterId);

    return new NodeAndInitialCredentials<ServerInDataCenter>(
        serverInDatacenter, serverInDataCenterId, serverCredentials);
  }