Amir Razmjou bio photo

Amir Razmjou

Researcher

Email Twitter Facebook Google+ LinkedIn Instagram Github Stackoverflow Last.fm Pinterest Foursquare Youtube

I still remember the time that W3C folks started to present the world with the phenonema of Web Services. During that time developers learned from frameworks like DCOM, CORBA, SOM the client/server architecture must relay on solid foundation, but the tie between the client and server must relay on a more relaxed and loose manner so service providers and service consumers can grow and change their functionality at desire without incuring intrupption. It makes me wonder how somtimes the main goal of making loose-coupled web services have forgotten and people started making tight-coupled libraries for these loose-coupled web-services to make SOA application development a bit easier.

Unlike current stream of virtualization technologies current OpenStack4j library is incapably adding or removing new network interface to running instances. OpenStack users have to embed Nova shell commands into their code to achieve the desired functionality. There are few drawbacks with this approach; one might be absence of OpenStack shell tools on the controller. Unfortunatly jCloud library also lacks the same functionality for current release candidate. I ended-up to use direct REST API calls to OpenStack REST API. We made some relaxation to problem, by reaching that goal through executing “curl” commands through shell so we needed a “Token”, “TenantID” and “Computing Service API Endpoint”. One could easily retrieve token and TenantID by running following shell command.

curl -i \
  -H "Content-Type: application/json" \
  -d '
{ "auth": {
    "identity": {
      "methods": ["password"],
      "password": {
        "user": {
          "name": "admin",
          "domain": { "id": "default" },
          "password": "adminpwd"
        }
      }
    }
  }
}' \
  http://localhost:5000/v3/auth/tokens

Adding network-interfaces to running instances through REST API was somewhat undocumented. But we knew that it’s feasible because nova shell command could do it. So I started by intercepting nova REST API calls that it sends to OpenStack controller by using –-debug keyboard before actual nova command.

nova --debug interface-attach --port-id UUID --net-id UUID UUID
nova --debug interface-attach --port-id UUID UUID

Once we concept-proof that by using plain curl commands we took things into Java and started to make REST API in Java.

/* Basic get method customized for nova API */
private HttpURLConnection getHttpURLConnection(String d, URL url, 
    String method, boolean doOutput) throws IOException {

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setDoOutput(doOutput);
    conn.setRequestMethod(method);
    conn.setRequestProperty("Accept", "application/json");
    conn.setRequestProperty("Content-Type", "application/json");
    conn.setRequestProperty("User-Agent", "python-novaclient");
    String token = getos().getToken().getId();
    conn.setRequestProperty("X-Auth-Token", token);

    if (!d.isEmpty()) {
        OutputStream os = conn.getOutputStream();
        os.write(d.getBytes());
        os.flush();
    }

    int responseCode = conn.getResponseCode();
    if (responseCode != HttpURLConnection.HTTP_CREATED &&
            responseCode != HttpURLConnection.HTTP_OK &&
            responseCode != HttpURLConnection.HTTP_ACCEPTED) {
        _logger.info("Failed : HTTP error code : " + responseCode +
                " URL: "  + url.toString() +
                " Response Message : " + conn.getResponseMessage());
    }
    return conn;
}

private HttpURLConnection getRestApiCall(URL url) throws IOException {
    return getHttpURLConnection("", url, "GET", false);
}

private HttpURLConnection deleteRestApiCall(URL url) throws IOException {
    return getHttpURLConnection("", url, "DELETE", false);
}

private HttpURLConnection postRestApiCall(String d, URL serverUrl) throws IOException {
    return getHttpURLConnection(d, serverUrl, "POST", true);
}

public String attachNetwork(String vmUUID, String _subnetId, String ip, String networkID) throws IOException {
    String d = String.format("{\"interfaceAttachment\": {\"fixed_ips\": [{\"ip_address\": \"%s\"}]," +
            " \"net_id\": \"%s\"}}", ip, networkID);
    URL serverUrl = new URL(String.format("%s/servers/%s/os-interface", _computeEndpoint, vmUUID));
    HttpURLConnection conn = postRestApiCall(d, serverUrl);

    BufferedReader br = new BufferedReader(new InputStreamReader(
            (conn.getInputStream())));
    JSONTokener jsonTokener = new JSONTokener(br);
    JSONObject jsonObject = new JSONObject(jsonTokener);
    String portId = jsonObject.getJSONObject("interfaceAttachment").getString("port_id");
    return portId;
}

public void detachNetwork(String vmUUID, String portId) throws IOException {
        URL serverUrl = new URL(String.format("%s/servers/%s/os-interface/%s", _computeEndpoint, vmUUID, portId));
        deleteRestApiCall(serverUrl);
}