Initial commit: VMware vSphere API simulator scaffold.
Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API contracts, docs, client examples, and the unit/integration/compatibility test suite for local client and tooling labs without a real vCenter.
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpRequest.BodyPublishers;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* Minimal Java 11+ cookbook against the vSphere REST gateway using a Basic-auth
|
||||
* session (vmware-api-session-id).
|
||||
*
|
||||
* javac Cookbook.java && java Cookbook
|
||||
*/
|
||||
public final class Cookbook {
|
||||
private static final HttpClient CLIENT =
|
||||
HttpClient.newBuilder().sslContext(insecureSslContext()).build();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String base = env("VSPHERE_BASE", "https://localhost");
|
||||
String user = env("VSPHERE_USER", "administrator@vsphere.local");
|
||||
String password = env("VSPHERE_PASSWORD", "VMware1!");
|
||||
String vmName = env("VSPHERE_VM_NAME", "java-lab-01");
|
||||
|
||||
String auth =
|
||||
"Basic " + Base64.getEncoder().encodeToString((user + ":" + password).getBytes(StandardCharsets.UTF_8));
|
||||
String sessionId = unquote(send(post(base + "/api/session", auth, null)));
|
||||
System.out.println("session: " + sessionId);
|
||||
|
||||
String vms = send(get(base + "/api/vcenter/vm", sessionId));
|
||||
System.out.println("vms before: " + vms.length() + " bytes");
|
||||
|
||||
String createBody =
|
||||
"{\"name\":\""
|
||||
+ vmName
|
||||
+ "\",\"guest_OS\":\"OTHER_GUEST_64\","
|
||||
+ "\"placement\":{\"folder\":\"group-v23\",\"host\":\"host-11\","
|
||||
+ "\"datastore\":\"datastore-31\",\"resource_pool\":\"resgroup-22\"},"
|
||||
+ "\"cpu\":{\"count\":1},\"memory\":{\"size_MiB\":512}}";
|
||||
String moid = unquote(send(postJson(base + "/api/vcenter/vm", sessionId, createBody)));
|
||||
System.out.println("created: " + moid);
|
||||
|
||||
String power = send(post(base + "/api/vcenter/vm/" + moid + "/power?action=start", sessionId, null));
|
||||
waitTask(base, sessionId, extractField(power, "task"));
|
||||
|
||||
String detail = send(get(base + "/api/vcenter/vm/" + moid, sessionId));
|
||||
System.out.println("power_state: " + extractField(detail, "power_state"));
|
||||
|
||||
power = send(post(base + "/api/vcenter/vm/" + moid + "/power?action=stop", sessionId, null));
|
||||
waitTask(base, sessionId, extractField(power, "task"));
|
||||
|
||||
send(delete(base + "/api/vcenter/vm/" + moid, sessionId));
|
||||
send(delete(base + "/api/session", sessionId));
|
||||
System.out.println("ok");
|
||||
}
|
||||
|
||||
private static void waitTask(String base, String sessionId, String task) throws Exception {
|
||||
if (task == null || task.isBlank()) {
|
||||
return;
|
||||
}
|
||||
long deadline = System.currentTimeMillis() + 120_000;
|
||||
while (System.currentTimeMillis() < deadline) {
|
||||
String body = send(get(base + "/api/cis/tasks/" + task, sessionId));
|
||||
String status = extractField(body, "status");
|
||||
if ("SUCCEEDED".equals(status) || "FAILED".equals(status)) {
|
||||
return;
|
||||
}
|
||||
Thread.sleep(300);
|
||||
}
|
||||
throw new IllegalStateException("timeout waiting for task " + task);
|
||||
}
|
||||
|
||||
private static String env(String key, String def) {
|
||||
String value = System.getenv(key);
|
||||
return value == null || value.isBlank() ? def : value;
|
||||
}
|
||||
|
||||
private static HttpRequest get(String uri, String sessionId) {
|
||||
return HttpRequest.newBuilder(URI.create(uri))
|
||||
.timeout(Duration.ofSeconds(60))
|
||||
.header("vmware-api-session-id", sessionId)
|
||||
.GET()
|
||||
.build();
|
||||
}
|
||||
|
||||
private static HttpRequest post(String uri, String auth, String body) {
|
||||
HttpRequest.Builder builder =
|
||||
HttpRequest.newBuilder(URI.create(uri)).timeout(Duration.ofSeconds(60));
|
||||
if (auth.startsWith("Basic ")) {
|
||||
builder.header("Authorization", auth);
|
||||
} else {
|
||||
builder.header("vmware-api-session-id", auth);
|
||||
}
|
||||
return builder.POST(body == null ? BodyPublishers.noBody() : BodyPublishers.ofString(body)).build();
|
||||
}
|
||||
|
||||
private static HttpRequest postJson(String uri, String sessionId, String body) {
|
||||
return HttpRequest.newBuilder(URI.create(uri))
|
||||
.timeout(Duration.ofSeconds(60))
|
||||
.header("vmware-api-session-id", sessionId)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(BodyPublishers.ofString(body))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static HttpRequest delete(String uri, String sessionId) {
|
||||
return HttpRequest.newBuilder(URI.create(uri))
|
||||
.timeout(Duration.ofSeconds(60))
|
||||
.header("vmware-api-session-id", sessionId)
|
||||
.DELETE()
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String send(HttpRequest request) throws Exception {
|
||||
HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (response.statusCode() >= 300) {
|
||||
throw new RuntimeException(response.statusCode() + ": " + response.body());
|
||||
}
|
||||
return response.body();
|
||||
}
|
||||
|
||||
private static String unquote(String value) {
|
||||
String trimmed = value.trim();
|
||||
if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) {
|
||||
return trimmed.substring(1, trimmed.length() - 1);
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
/** Tiny string-based field extraction — good enough for a lab smoke, no JSON library required. */
|
||||
private static String extractField(String body, String field) {
|
||||
String marker = "\"" + field + "\":";
|
||||
int start = body.indexOf(marker);
|
||||
if (start < 0) {
|
||||
return "";
|
||||
}
|
||||
start += marker.length();
|
||||
while (start < body.length() && (body.charAt(start) == ' ' || body.charAt(start) == '"')) {
|
||||
start++;
|
||||
}
|
||||
int end = start;
|
||||
while (end < body.length() && body.charAt(end) != '"' && body.charAt(end) != ',' && body.charAt(end) != '}') {
|
||||
end++;
|
||||
}
|
||||
return body.substring(start, end);
|
||||
}
|
||||
|
||||
private static javax.net.ssl.SSLContext insecureSslContext() {
|
||||
try {
|
||||
javax.net.ssl.TrustManager[] trustAll =
|
||||
new javax.net.ssl.TrustManager[] {
|
||||
new javax.net.ssl.X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[0];
|
||||
}
|
||||
|
||||
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
|
||||
|
||||
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
|
||||
}
|
||||
};
|
||||
javax.net.ssl.SSLContext context = javax.net.ssl.SSLContext.getInstance("TLS");
|
||||
context.init(null, trustAll, new java.security.SecureRandom());
|
||||
return context;
|
||||
} catch (Exception error) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user