package com.undatech.opaque.proxmox; import android.R.bool; import android.R.integer; import android.os.Handler; import com.undatech.opaque.ConnectionSettings; import com.undatech.opaque.proxmox.pojo.*; import java.io.IOException; import java.lang.reflect.Array; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.security.auth.login.LoginException; import org.apache.http.HttpException; import org.apache.http.conn.HttpHostConnectException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class ProxmoxClient extends RestClient { private static final String TAG = "RestClient"; private String baseUrl; private String ticket; private String csrfToken; /** * Initializes a connection to PVE's API. * @param hostname the hostname of PVE * @param user user to authenticate with * @param realm authentication realm, e.g. PAM * @param password password to authenticate with * @throws JSONException * @throws IOException * @throws HttpException * @throws LoginException */ public ProxmoxClient(String hostname, String user, String realm, String password, ConnectionSettings connection, Handler handler) throws JSONException, IOException, HttpException, LoginException { super(connection, handler); this.baseUrl = "https://" + hostname + ":8006/api2/json"; // Ensure we have let the user approve the certificate //if (connection.getOvirtCaData().isEmpty()) { // android.util.Log.i(TAG, "Connecting over SSL to get the certificate."); // resetState(baseUrl); // execute(RestClient.RequestMethod.GET); //} resetState(baseUrl + "/access/ticket"); addParam("username", user); addParam("password", password); addParam("realm", realm); execute(RestClient.RequestMethod.POST); if (getResponseCode() == HttpURLConnection.HTTP_OK) { JSONObject data = new JSONObject(getResponse()) .getJSONObject("data"); ticket = data.getString("ticket"); csrfToken = data.getString("CSRFPreventionToken"); } else if (getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new LoginException(getErrorMessage()); } else { throw new HttpException(getErrorMessage()); } } /** * Actually performs a request to PVE. * @param resource the REST resource to affect * @param method the method (GET, POST, etc) * @param requestData the data to send * @return the data returned by PVE as a result of the request. * @throws JSONException * @throws LoginException * @throws IOException * @throws HttpException */ private JSONObject request(String resource, RestClient.RequestMethod method, Map<String, String> requestData) throws IOException, JSONException, LoginException, HttpException { resetState(baseUrl + resource); addHeader("Cookie", "PVEAuthCookie=" + ticket); if (!method.equals(RestClient.RequestMethod.GET)) { addHeader("CSRFPreventionToken", csrfToken); } if (requestData != null) for (Map.Entry<String, String> p : requestData.entrySet()) { addParam(p.getKey(), p.getValue()); } execute(method); if (isSuccessfulCode(getResponseCode())) { return new JSONObject(getResponse()); } else if (getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new LoginException(getErrorMessage()); } else { // The API returned an unexpected 4xx or 5xx return code. throw new HttpException(getErrorMessage()); } } /** * Checks if an HTTP code is successful (200 - 299) or not */ boolean isSuccessfulCode (int code) { return (code/100 == 2); } /** * Retrieves a representation of the VNC display of a node. * @param node the name of the PVE node * @return the VNC display of the node * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public VncDisplay vncNode(String node) throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/nodes/" + node + "/vncshell", RestClient.RequestMethod.POST, null); return new VncDisplay(jObj.getJSONObject("data")); } /** * Retrieves a representation of the SPICE display of a node. * @param node the name of the PVE node * @return the SPICE display of the node * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public SpiceDisplay spiceNode(String node) throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/nodes/" + node + "/spiceshell", RestClient.RequestMethod.POST, null); return new SpiceDisplay(jObj.getJSONObject("data")); } /** * Retrieves a representation of the VNC display of a VM. * @param node the name of the PVE node * @param type of VM, one of qemu, lxc, or openvz (deprecated) * @param vmid the numeric VM ID * @return The VNC display of the VM * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public VncDisplay vncVm(String node, String type, int vmid) throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/nodes/" + node + "/" + type + "/" + vmid + "/vncproxy", RestClient.RequestMethod.POST, null); return new VncDisplay(jObj.getJSONObject("data")); } /** * Retrieves a representation of the SPICE display of a VM. * @param node the name of the PVE node * @param type of VM, one of qemu, lxc, or openvz (deprecated) * @param vmid the numeric VM ID * @return The SPICE display of the VM * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public SpiceDisplay spiceVm(String node, String type, int vmid) throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/nodes/" + node + "/" + type + "/" + vmid + "/spiceproxy", RestClient.RequestMethod.POST, null); return new SpiceDisplay(jObj.getJSONObject("data")); } /** * Starts a VM. * @param node the name of the PVE node * @param type of VM, one of qemu, lxc, or openvz (deprecated) * @param vmid the numeric VM ID * @return status string * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public String startVm(String node, String type, int vmid) throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/nodes/" + node + "/" + type + "/" + vmid + "/status/start", RestClient.RequestMethod.POST, null); return jObj.getString("data"); } /** * Gets the current status of a VM. * @param node the name of the PVE node * @param type of VM, one of qemu, lxc, or openvz (deprecated) * @param vmid the numeric VM ID * @return status of VM * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public VmStatus getCurrentStatus(String node, String type, int vmid) throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/nodes/" + node + "/" + type + "/" + vmid + "/status/current", RestClient.RequestMethod.GET, null); return new VmStatus(jObj.getJSONObject("data")); } /** * Shows what resources are currently available on the PVE cluster * @return object representing resources available * @throws LoginException * @throws JSONException * @throws IOException * @throws HttpException */ public Map<String, PveResource> getResources() throws LoginException, JSONException, IOException, HttpException { JSONObject jObj = request("/cluster/resources", RestClient.RequestMethod.GET, null); JSONArray jArr = jObj.getJSONArray("data"); HashMap<String, PveResource> result = new HashMap<String, PveResource>(); for (int i = 0; i < jArr.length(); i++) { PveResource r = new PveResource(jArr.getJSONObject(i)); if (r.getName() != null && r.getNode() != null && r.getType() != null && r.getVmid() != null) { result.put(r.getNode() + "/" + r.getType() + "/" + r.getVmid(), r); } } return result; } }