1+ //
2+ // Licensed to the Apache Software Foundation (ASF) under one
3+ // or more contributor license agreements. See the NOTICE file
4+ // distributed with this work for additional information
5+ // regarding copyright ownership. The ASF licenses this file
6+ // to you under the Apache License, Version 2.0 (the
7+ // "License"); you may not use this file except in compliance
8+ // with the License. You may obtain a copy of the License at
9+ //
10+ // http://www.apache.org/licenses/LICENSE-2.0
11+ //
12+ // Unless required by applicable law or agreed to in writing,
13+ // software distributed under the License is distributed on an
14+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+ // KIND, either express or implied. See the License for the
16+ // specific language governing permissions and limitations
17+ // under the License.
18+ //
19+
20+ package com .cloud .network .nicira ;
21+
22+ import java .io .IOException ;
23+ import java .util .HashMap ;
24+ import java .util .Map ;
25+
26+ import org .apache .http .HttpEntity ;
27+ import org .apache .http .StatusLine ;
28+ import org .apache .http .client .methods .CloseableHttpResponse ;
29+ import org .apache .http .client .methods .HttpUriRequest ;
30+ import org .apache .http .client .protocol .HttpClientContext ;
31+ import org .apache .http .impl .client .CloseableHttpClient ;
32+ import org .apache .http .util .EntityUtils ;
33+ import org .apache .log4j .Logger ;
34+
35+ import com .cloud .utils .rest .BasicRestClient ;
36+ import com .cloud .utils .rest .CloudstackRESTException ;
37+ import com .cloud .utils .rest .HttpConstants ;
38+ import com .cloud .utils .rest .HttpMethods ;
39+ import com .cloud .utils .rest .HttpStatusCodeHelper ;
40+ import com .cloud .utils .rest .HttpUriRequestBuilder ;
41+
42+ public class NiciraRestClient extends BasicRestClient {
43+
44+ private static final Logger s_logger = Logger .getLogger (NiciraRestClient .class );
45+
46+ private static final String CONTENT_TYPE = HttpConstants .CONTENT_TYPE ;
47+ private static final String TEXT_HTML_CONTENT_TYPE = HttpConstants .TEXT_HTML_CONTENT_TYPE ;
48+
49+ private static final int DEFAULT_BODY_RESP_MAX_LEN = 1024 ;
50+ private static final int DEFAULT_EXECUTION_LIMIT = 5 ;
51+
52+ private final ExecutionCounter counter ;
53+ private final int maxResponseErrorMesageLength ;
54+ private final int executionLimit ;
55+
56+ private final String username ;
57+ private final String password ;
58+ private final String loginUrl ;
59+
60+ private NiciraRestClient (final Builder builder ) {
61+ super (builder .client , builder .clientContext , builder .hostname );
62+ executionLimit = builder .executionLimit ;
63+ counter = new ExecutionCounter (executionLimit );
64+ maxResponseErrorMesageLength = builder .maxResponseErrorMesageLength ;
65+ username = builder .username ;
66+ password = builder .password ;
67+ loginUrl = builder .loginUrl ;
68+ }
69+
70+ public static Builder create () {
71+ return new Builder ();
72+ }
73+
74+ @ Override
75+ public CloseableHttpResponse execute (final HttpUriRequest request ) throws CloudstackRESTException {
76+ return execute (request , 0 );
77+ }
78+
79+ private CloseableHttpResponse execute (final HttpUriRequest request , final int previousStatusCode ) throws CloudstackRESTException {
80+ if (counter .hasReachedExecutionLimit ()) {
81+ throw new CloudstackRESTException ("Reached max executions limit of " + executionLimit );
82+ }
83+ counter .incrementExecutionCounter ();
84+ s_logger .debug ("Executing " + request .getMethod () + " request [execution count = " + counter .getValue () + "]" );
85+ final CloseableHttpResponse response = super .execute (request );
86+
87+ final StatusLine statusLine = response .getStatusLine ();
88+ final int statusCode = statusLine .getStatusCode ();
89+ s_logger .debug ("Status of last request: " + statusLine .toString ());
90+ if (HttpStatusCodeHelper .isUnauthorized (statusCode )) {
91+ return handleUnauthorizedResponse (request , previousStatusCode , response , statusCode );
92+ } else if (HttpStatusCodeHelper .isSuccess (statusCode )) {
93+ return handleSuccessResponse (response );
94+ } else {
95+ throw new CloudstackRESTException ("Unexpecetd status code: " + statusCode );
96+ }
97+ }
98+
99+ private CloseableHttpResponse handleUnauthorizedResponse (final HttpUriRequest request , final int previousStatusCode , final CloseableHttpResponse response , final int statusCode )
100+ throws CloudstackRESTException {
101+ super .closeResponse (response );
102+ if (HttpStatusCodeHelper .isUnauthorized (previousStatusCode )) {
103+ s_logger .error (responseToErrorMessage (response ));
104+ throw new CloudstackRESTException ("Two consecutive failed attempts to authenticate against REST server" );
105+ }
106+ final HttpUriRequest authenticateRequest = createAuthenticationRequest ();
107+ final CloseableHttpResponse loginResponse = execute (authenticateRequest , statusCode );
108+ final int loginStatusCode = loginResponse .getStatusLine ().getStatusCode ();
109+ super .closeResponse (loginResponse );
110+ return execute (request , loginStatusCode );
111+ }
112+
113+ private CloseableHttpResponse handleSuccessResponse (final CloseableHttpResponse response ) {
114+ counter .resetExecutionCounter ();
115+ return response ;
116+ }
117+
118+ private HttpUriRequest createAuthenticationRequest () {
119+ final Map <String , String > parameters = new HashMap <>();
120+ parameters .put ("username" , username );
121+ parameters .put ("password" , password );
122+ return HttpUriRequestBuilder .create ()
123+ .method (HttpMethods .POST )
124+ .methodParameters (parameters )
125+ .path (loginUrl )
126+ .build ();
127+ }
128+
129+ private String responseToErrorMessage (final CloseableHttpResponse response ) {
130+ String errorMessage = response .getStatusLine ().toString ();
131+ if (response .containsHeader (CONTENT_TYPE ) && TEXT_HTML_CONTENT_TYPE .equals (response .getFirstHeader (CONTENT_TYPE ).getValue ())) {
132+ try {
133+ final HttpEntity entity = response .getEntity ();
134+ final String respobnseBody = EntityUtils .toString (entity );
135+ errorMessage = respobnseBody .subSequence (0 , maxResponseErrorMesageLength ).toString ();
136+ } catch (final IOException e ) {
137+ s_logger .debug ("Could not read repsonse body. Response: " + response , e );
138+ }
139+ }
140+
141+ return errorMessage ;
142+ }
143+
144+ protected static class Builder extends BasicRestClient .Builder <Builder > {
145+ private CloseableHttpClient client ;
146+ private HttpClientContext clientContext ;
147+ private String hostname ;
148+ private String username ;
149+ private String password ;
150+ private String loginUrl ;
151+ private int executionLimit = DEFAULT_EXECUTION_LIMIT ;
152+ private int maxResponseErrorMesageLength = DEFAULT_BODY_RESP_MAX_LEN ;
153+
154+ public Builder hostname (final String hostname ) {
155+ this .hostname = hostname ;
156+ return this ;
157+ }
158+
159+ public Builder username (final String username ) {
160+ this .username = username ;
161+ return this ;
162+ }
163+
164+ public Builder password (final String password ) {
165+ this .password = password ;
166+ return this ;
167+ }
168+
169+ public Builder loginUrl (final String loginUrl ) {
170+ this .loginUrl = loginUrl ;
171+ return this ;
172+ }
173+
174+ @ Override
175+ public Builder client (final CloseableHttpClient client ) {
176+ this .client = client ;
177+ return this ;
178+ }
179+
180+ @ Override
181+ public Builder clientContext (final HttpClientContext clientContext ) {
182+ this .clientContext = clientContext ;
183+ return this ;
184+ }
185+
186+ public Builder executionLimit (final int executionLimit ) {
187+ this .executionLimit = executionLimit ;
188+ return this ;
189+ }
190+
191+ public Builder maxResponseErrorMesageLength (final int maxResponseErrorMesageLength ) {
192+ this .maxResponseErrorMesageLength = maxResponseErrorMesageLength ;
193+ return this ;
194+ }
195+
196+ @ Override
197+ public NiciraRestClient build () {
198+ return new NiciraRestClient (this );
199+ }
200+
201+ }
202+ }
0 commit comments