/*
* Copyright 2010 Proofpoint, Inc.
*
* Licensed 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.proofpoint.cloudmanagement.service.inventoryclient;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.proofpoint.cloudmanagement.service.InstanceCreationNotifier;
import com.proofpoint.http.client.HttpClient;
import com.proofpoint.http.client.JsonBodyGenerator;
import com.proofpoint.http.client.JsonResponseHandler;
import com.proofpoint.http.client.Request;
import com.proofpoint.http.client.StatusResponseHandler;
import com.proofpoint.http.client.StatusResponseHandler.StatusResponse;
import com.proofpoint.json.JsonCodec;
import com.proofpoint.log.Logger;
import org.jclouds.encryption.internal.Base64;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.proofpoint.http.client.Request.Builder.prepareGet;
import static com.proofpoint.http.client.Request.Builder.preparePut;
public class InventoryClient implements InstanceCreationNotifier
{
private final HttpClient client;
private final URI inventoryHost;
private final String authorization;
private final LoadingCache<String, InventorySystem> inventoryCache;
private static final Logger log = Logger.get(InventoryClient.class);
private static final JsonCodec<InventorySystem> SYSTEM_DATA_CODEC = JsonCodec.jsonCodec(InventorySystem.class);
private static final JsonCodec<Map<String, String>> MAP_JSON_CODEC = JsonCodec.mapJsonCodec(String.class, String.class);
@Inject
public InventoryClient(InventoryClientConfig config, @Inventory HttpClient client)
{
this.inventoryHost = config.getInventoryUri();
this.authorization = basicAuthEncode(config.getUserId(), config.getPassword());
this.client = client;
this.inventoryCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(new CacheLoader<String, InventorySystem>()
{
@Override
public InventorySystem load(String id)
throws Exception
{
String inventoryName = getPcmSystemName(id);
return getSystem(inventoryName);
}
});
}
public InventorySystem getSystemByInstanceId(String instanceId)
{
return inventoryCache.getUnchecked(instanceId);
}
public void notifyInstanceCreated(String instanceId)
{
try {
String inventoryName = getPcmSystemName(instanceId);
InventorySystem inventorySystem = new InventorySystem(inventoryName);
inventorySystem.setPicInstance(instanceId);
patchSystem(inventorySystem);
}
catch (Exception e) {
log.error("Expected to get a server name from inventory for instanceId [" + instanceId + "] but caught exception " + e.getMessage(), e);
throw Throwables.propagate(e);
}
}
public String getPcmSystemName(String instanceId)
throws Exception
{
Preconditions.checkNotNull(instanceId, "instanceId is null");
Request request = prepareGet()
.setUri(UriBuilder.fromUri(inventoryHost).path("/pcmsystemname/{instanceId}").build(instanceId))
.setHeader(HttpHeaders.AUTHORIZATION, authorization)
.build();
Map<String, String> response = client.execute(request, JsonResponseHandler.createJsonResponseHandler(MAP_JSON_CODEC));
return response.get("fqdn");
}
public InventorySystem getSystem(String systemName)
throws Exception
{
Preconditions.checkArgument(!Strings.isNullOrEmpty(systemName), "systemName is required");
Preconditions.checkArgument(!Strings.isNullOrEmpty(authorization), "authToken is required");
Request request = prepareGet()
.setUri(UriBuilder.fromUri(inventoryHost).path("/system/{system}").build(systemName))
.setHeader("Authorization", authorization)
.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
.build();
try {
return client.execute(request, JsonResponseHandler.createJsonResponseHandler(SYSTEM_DATA_CODEC));
}
catch (Exception e) {
performRequestAndValidateStatusForBodylessRequests(request);
}
return null;
}
public void patchSystem(InventorySystem inventorySystem)
throws Exception
{
Preconditions.checkNotNull(inventorySystem, "inventorySystem is null");
Request request = preparePut()
.setUri(UriBuilder.fromUri(inventoryHost).path("/system/{system}").build(inventorySystem.getFqdn()))
.setHeader(HttpHeaders.AUTHORIZATION, authorization)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
.setBodyGenerator(JsonBodyGenerator.jsonBodyGenerator(SYSTEM_DATA_CODEC, inventorySystem))
.build();
log.info("Patch Request To Inventory [" + request + "] with object [" + inventorySystem + "]");
performRequestAndValidateStatusForBodylessRequests(request);
}
private void performRequestAndValidateStatusForBodylessRequests(Request request)
{
StatusResponse response = client.execute(request, StatusResponseHandler.createStatusResponseHandler());
if (!ImmutableList.of(200, 204).contains(response.getStatusCode())) {
throw new RuntimeException(String.format("Request failed with code %d: Body -->|%s|<--", response.getStatusCode(), response.getStatusMessage()));
}
}
@VisibleForTesting
static String basicAuthEncode(String user, String pass)
{
return String.format("Basic %s",
Base64.encodeBytes(String.format("%s:%s", user, pass).getBytes(Charsets.UTF_8)));
}
}