blob: f9c86be9d08fc4da3c36af43db3415a0d8a1b29b [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package com.cloud.network.nicira;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import com.cloud.utils.rest.BasicRestClient;
import com.cloud.utils.rest.CloudstackRESTException;
import com.cloud.utils.rest.HttpConstants;
import com.cloud.utils.rest.HttpMethods;
import com.cloud.utils.rest.HttpStatusCodeHelper;
import com.cloud.utils.rest.HttpUriRequestBuilder;
public class NiciraRestClient extends BasicRestClient {
private static final String CONTENT_TYPE = HttpConstants.CONTENT_TYPE;
private static final String TEXT_HTML_CONTENT_TYPE = HttpConstants.TEXT_HTML_CONTENT_TYPE;
private static final int DEFAULT_BODY_RESP_MAX_LEN = 1024;
private static final int DEFAULT_EXECUTION_LIMIT = 5;
private final ExecutionCounter counter;
private final int maxResponseErrorMesageLength;
private final int executionLimit;
private final String username;
private final String password;
private final String loginUrl;
private NiciraRestClient(final Builder builder) {
super(builder.client, builder.clientContext, builder.hostname);
executionLimit = builder.executionLimit;
counter = new ExecutionCounter(executionLimit);
maxResponseErrorMesageLength = builder.maxResponseErrorMesageLength;
username = builder.username;
password = builder.password;
loginUrl = builder.loginUrl;
}
public static Builder create() {
return new Builder();
}
@Override
public CloseableHttpResponse execute(final HttpUriRequest request) throws CloudstackRESTException {
return execute(request, 0);
}
CloseableHttpResponse execute(final HttpUriRequest request, final int previousStatusCode) throws CloudstackRESTException {
if (counter.hasReachedExecutionLimit()) {
throw new CloudstackRESTException("Reached max executions limit of " + executionLimit);
}
counter.incrementExecutionCounter();
logger.debug("Executing " + request.getMethod() + " request [execution count = " + counter.getValue() + "]");
final CloseableHttpResponse response = super.execute(request);
final StatusLine statusLine = response.getStatusLine();
final int statusCode = statusLine.getStatusCode();
logger.debug("Status of last request: " + statusLine.toString());
if (HttpStatusCodeHelper.isUnauthorized(statusCode)) {
return handleUnauthorizedResponse(request, previousStatusCode, response, statusCode);
} else if (HttpStatusCodeHelper.isSuccess(statusCode)) {
return handleSuccessResponse(request, response);
} else if (HttpStatusCodeHelper.isConflict(statusCode)) {
throw new CloudstackRESTException("Conflict: " + statusLine.getReasonPhrase(), statusCode);
} else {
throw new CloudstackRESTException("Unexpected status code: " + statusCode, statusCode);
}
}
private CloseableHttpResponse handleUnauthorizedResponse(final HttpUriRequest request, final int previousStatusCode, final CloseableHttpResponse response, final int statusCode)
throws CloudstackRESTException {
super.closeResponse(response);
if (HttpStatusCodeHelper.isUnauthorized(previousStatusCode)) {
logger.error(responseToErrorMessage(response));
throw new CloudstackRESTException("Two consecutive failed attempts to authenticate against REST server");
}
final HttpUriRequest authenticateRequest = createAuthenticationRequest();
final CloseableHttpResponse loginResponse = execute(authenticateRequest, statusCode);
final int loginStatusCode = loginResponse.getStatusLine().getStatusCode();
super.closeResponse(loginResponse);
return execute(request, loginStatusCode);
}
private CloseableHttpResponse handleSuccessResponse(final HttpUriRequest request, final CloseableHttpResponse response) {
if (!request.getURI().getPath().contains(loginUrl)) {
counter.resetExecutionCounter();
}
return response;
}
private HttpUriRequest createAuthenticationRequest() {
final Map<String, String> parameters = new LinkedHashMap<>();
parameters.put("username", username);
parameters.put("password", password);
return HttpUriRequestBuilder.create()
.method(HttpMethods.POST)
.methodParameters(parameters)
.path(loginUrl)
.build();
}
private String responseToErrorMessage(final CloseableHttpResponse response) {
String errorMessage = response.getStatusLine().toString();
if (response.containsHeader(CONTENT_TYPE) && TEXT_HTML_CONTENT_TYPE.equals(response.getFirstHeader(CONTENT_TYPE).getValue())) {
try {
final HttpEntity entity = response.getEntity();
final String respobnseBody = EntityUtils.toString(entity);
errorMessage = respobnseBody.subSequence(0, maxResponseErrorMesageLength).toString();
} catch (final IOException e) {
logger.debug("Could not read response body. Response: " + response, e);
}
}
return errorMessage;
}
protected static class Builder extends BasicRestClient.Builder<Builder> {
private CloseableHttpClient client;
private HttpClientContext clientContext;
private String hostname;
private String username;
private String password;
private String loginUrl;
private int executionLimit = DEFAULT_EXECUTION_LIMIT;
private int maxResponseErrorMesageLength = DEFAULT_BODY_RESP_MAX_LEN;
public Builder hostname(final String hostname) {
this.hostname = hostname;
return this;
}
public Builder username(final String username) {
this.username = username;
return this;
}
public Builder password(final String password) {
this.password = password;
return this;
}
public Builder loginUrl(final String loginUrl) {
this.loginUrl = loginUrl;
return this;
}
@Override
public Builder client(final CloseableHttpClient client) {
this.client = client;
return this;
}
@Override
public Builder clientContext(final HttpClientContext clientContext) {
this.clientContext = clientContext;
return this;
}
public Builder executionLimit(final int executionLimit) {
this.executionLimit = executionLimit;
return this;
}
public Builder maxResponseErrorMesageLength(final int maxResponseErrorMesageLength) {
this.maxResponseErrorMesageLength = maxResponseErrorMesageLength;
return this;
}
@Override
public NiciraRestClient build() {
return new NiciraRestClient(this);
}
}
}