/*-
* ============LICENSE_START=======================================================
* openECOMP : APP-C
* ================================================================================
* Copyright (C) 2017 AT&T Intellectual Property. All rights
* reserved.
* ================================================================================
* 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.
* ============LICENSE_END=========================================================
*/
package org.openecomp.appc.adapter.iaas.impl;
import com.woorea.openstack.base.client.OpenStackBaseException;
import com.woorea.openstack.heat.Heat;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.openecomp.appc.Constants;
import org.openecomp.appc.adapter.iaas.ProviderAdapter;
import org.openecomp.appc.adapter.openstack.heat.SnapshotResource;
import org.openecomp.appc.adapter.openstack.heat.StackResource;
import org.openecomp.appc.adapter.openstack.heat.model.CreateSnapshotParams;
import org.openecomp.appc.adapter.openstack.heat.model.Snapshot;
import org.openecomp.appc.configuration.Configuration;
import org.openecomp.appc.configuration.ConfigurationFactory;
import org.openecomp.appc.exceptions.APPCException;
import org.openecomp.appc.exceptions.UnknownProviderException;
import org.openecomp.appc.i18n.Msg;
import org.openecomp.appc.pool.Pool;
import org.openecomp.appc.pool.PoolExtensionException;
import org.openecomp.appc.util.StructuredPropertyHelper;
import org.openecomp.appc.util.StructuredPropertyHelper.Node;
import com.att.cdp.exceptions.*;
import com.att.cdp.openstack.OpenStackContext;
import com.att.cdp.openstack.connectors.HeatConnector;
import com.att.cdp.openstack.util.ExceptionMapper;
import com.att.cdp.pal.util.StringHelper;
import com.att.cdp.zones.*;
import com.att.cdp.zones.model.Image;
import com.att.cdp.zones.model.Server;
import com.att.cdp.zones.model.ServerBootSource;
import com.att.cdp.zones.model.Stack;
import com.att.cdp.zones.model.Server.Status;
import com.att.cdp.zones.spi.AbstractService;
import com.att.cdp.zones.spi.RequestState;
import com.att.eelf.configuration.EELFLogger;
import com.att.eelf.configuration.EELFManager;
import com.att.eelf.i18n.EELFResourceManager;
import org.openecomp.sdnc.sli.SvcLogicContext;
import org.slf4j.MDC;
import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
/**
* This class implements the {@link ProviderAdapter} interface. This interface defines the behaviors that our service
* provides.
*/
@SuppressWarnings("javadoc")
public class ProviderAdapterImpl implements ProviderAdapter {
/**
* The name of the adapter
*/
@SuppressWarnings("nls")
private static final String ADAPTER_NAME = "Appc IaaS Adapter";
/**
* The username and password to use for dynamically created connections
*/
private static String DEFAULT_USER;
private static String DEFAULT_PASS;
/**
* The constant used to define the adapter name in the mapped diagnostic context
*/
@SuppressWarnings("nls")
private static final String MDC_ADAPTER = "adapter";
/**
* The constant used to define the service name in the mapped diagnostic context
*/
@SuppressWarnings("nls")
static final String MDC_SERVICE = "service";
/**
* The constant for the status code for a failed outcome
*/
@SuppressWarnings("nls")
private static final String OUTCOME_FAILURE = "failure";
/**
* The constant for the status code for a successful outcome
*/
@SuppressWarnings("nls")
private static final String OUTCOME_SUCCESS = "success";
/**
* A constant for the property token "provider" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER = "provider";
/**
* A constant for the property token "identity" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER_IDENTITY = "identity";
/**
* A constant for the property token "tenant" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER_TENANT = "tenant";
/**
* A constant for the property token "tenant name" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER_TENANT_NAME = "name";
/**
* A constant for the property token "password" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER_TENANT_PASSWORD = "password"; // NOSONAR
/**
* A constant for the property token "userid" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER_TENANT_USERID = "userid";
/**
* A constant for the property token "type" used in the structured property specifications
*/
@SuppressWarnings("nls")
private static final String PROPERTY_PROVIDER_TYPE = "type";
/**
* The name of the service to evacuate a server
*/
@SuppressWarnings("nls")
private static final String EVACUATE_SERVICE = "evacuateServer";
/**
* The name of the service to migrate a server
*/
@SuppressWarnings("nls")
private static final String MIGRATE_SERVICE = "migrateServer";
/**
* The name of the service to rebuild a server
*/
@SuppressWarnings("nls")
private static final String REBUILD_SERVICE = "rebuildServer";
/**
* The name of the service to restart a server
*/
@SuppressWarnings("nls")
private static final String RESTART_SERVICE = "restartServer";
/**
* The name of the service to check status of a server
*/
@SuppressWarnings("nls")
private static final String VMSTATUSCHECK_SERVICE = "vmStatuschecker";
/**
* The name of the service to restart a server
*/
@SuppressWarnings("nls")
private static final String SNAPSHOT_SERVICE = "createSnapshot";
/**
* The name of the service to terminate a stack
*/
@SuppressWarnings("nls")
private static final String TERMINATE_STACK = "terminateStack";
/**
* The name of the service to snapshot a stack
*/
@SuppressWarnings("nls")
private static final String SNAPSHOT_STACK = "snapshotStack";
/**
* The name of a service to start a server
*/
@SuppressWarnings("nls")
private static final String START_SERVICE = "startServer";
/**
* The name of the service to stop a server
*/
@SuppressWarnings("nls")
private static final String STOP_SERVICE = "stopServer";
/**
* The name of the service to stop a server
*/
@SuppressWarnings("nls")
private static final String TERMINATE_SERVICE = "terminateServer";
/**
* The name of the service to lookup a server
*/
@SuppressWarnings("nls")
private static final String LOOKUP_SERVICE = "lookupServer";
/**
* The logger to be used
*/
private static final EELFLogger logger = EELFManager.getInstance().getLogger(ProviderAdapterImpl.class);
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* The constant for a left parenthesis
*/
private static final char LPAREN = '(';
/**
* The constant for a new line control code
*/
private static final char NL = '\n';
/**
* The constant for a single quote
*/
private static final char QUOTE = '\'';
/**
* The constant for a right parenthesis
*/
private static final char RPAREN = ')';
/**
* The constant for a space
*/
private static final char SPACE = ' ';
/**
* A reference to the adapter configuration object.
*/
private Configuration configuration;
/**
* A cache of providers that are predefined.
*/
private Map providerCache;
/**
* A list of valid initial VM statuses for a migrate operations
*/
private static final Collection migratableStatuses = Arrays.asList(Status.READY, Status.RUNNING, Status.SUSPENDED);
/**
* This default constructor is used as a work around because the activator wasnt getting called
*/
@SuppressWarnings("all")
public ProviderAdapterImpl() {
initialize();
}
/**
* This constructor is used primarily in the test cases to bypass initialization of the adapter for isolated,
* disconnected testing
*
* @param initialize
* True if the adapter is to be initialized, can false if not
*/
@SuppressWarnings("all")
public ProviderAdapterImpl(boolean initialize) {
configuration = ConfigurationFactory.getConfiguration();
if (initialize) {
initialize();
}
}
/**
* @param props
* not used
*/
public ProviderAdapterImpl(@SuppressWarnings("unused") Properties props) {
initialize();
}
/**
* Returns the symbolic name of the adapter
*
* @return The adapter name
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#getAdapterName()
*/
@Override
public String getAdapterName() {
return configuration.getProperty(Constants.PROPERTY_ADAPTER_NAME);
}
@SuppressWarnings("nls")
@Override
public Image createSnapshot(Map params, SvcLogicContext ctx) throws APPCException {
Image snapshot = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, SNAPSHOT_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:Snapshot");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.SNAPSHOTING_SERVER, appName);
String msg;
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
debugParameters(params);
debugContext(ctx);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
Server server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
if (hasImageAccess(rc, context)) {
snapshot = createSnapshot(rc, server);
doSuccess(rc);
} else {
msg = EELFResourceManager.format(Msg.REBUILD_SERVER_FAILED, server.getName(), server.getId(),
"Accessing Image Service Failed");
logger.error(msg);
doFailure(rc, HttpStatus.FORBIDDEN_403, msg);
}
context.close();
}
} catch (ResourceNotFoundException e) {
msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
SNAPSHOT_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return snapshot;
}
private boolean validateVM(RequestContext rc, String appName, String vm_url, VMURL vm)
throws RequestFailedException {
String msg;
if (vm == null) {
msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
return true;
}
validateVMURL(vm);
return false;
}
private Image createSnapshot(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
Context context = server.getContext();
Provider provider = context.getProvider();
ImageService service = context.getImageService(); // Already checked access by this point
String snapshotName = generateSnapshotName(server.getName());
logger.info(String.format("Creating snapshot of server %s (%s) with name %s", server.getName(), server.getId(),
snapshotName));
// Request Snapshot
String msg;
while (rc.attempt()) {
try {
server.createSnapshot(snapshotName);
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Stop Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
// Locate snapshot image
Image snapshot = null;
while (rc.attempt()) {
try {
snapshot = service.getImageByName(snapshotName);
if (snapshot != null) {
break;
}
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Stop Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
// Wait for it to be ready
waitForStateChange(rc, snapshot, Image.Status.ACTIVE);
return snapshot;
}
private String generateSnapshotName(String server) {
SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
return String.format("Snapshot of %s at %s", server, df.format(new Date()));
}
/**
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#evacuateServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server evacuateServer(Map params, SvcLogicContext ctx) throws APPCException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, EVACUATE_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:Evacuate");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.EVACUATING_SERVER, appName);
String msg;
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
String providerName = params.get(ProviderAdapter.PROPERTY_PROVIDER_NAME);
debugParameters(params);
debugContext(ctx);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
Context context = null;
try {
context = getContext(rc, vm_url, providerName);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
evacuateServer(rc, server);
server.refreshStatus();
context.close();
doSuccess(rc);
}
} catch (ResourceNotFoundException e) {
msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
EVACUATE_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
/**
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#migrateServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server migrateServer(Map params, SvcLogicContext ctx) throws APPCException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, MIGRATE_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:Migrate");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.MIGRATING_SERVER, appName);
String msg;
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
debugParameters(params);
debugContext(ctx);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
migrateServer(rc, server);
server.refreshStatus();
context.close();
doSuccess(rc);
}
} catch (ResourceNotFoundException e) {
msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
MIGRATE_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
private void evacuateServer(RequestContext rc, @SuppressWarnings("unused") Server server) throws ZoneException, RequestFailedException {
doFailure(rc, HttpStatus.NOT_IMPLEMENTED_501, "The operation 'EVACUATE' is not yet implemented");
}
private void migrateServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
String msg;
Context ctx = server.getContext();
ComputeService service = ctx.getComputeService();
// Init status will equal final status
Status initialStatus = server.getStatus();
if (initialStatus == null) {
throw new ZoneException("Failed to determine server's starting status");
}
// We can only migrate certain statuses
if (!migratableStatuses.contains(initialStatus)) {
throw new ZoneException(String.format("Cannot migrate server that is in %s state. Must be in one of [%s]",
initialStatus, migratableStatuses));
}
boolean inConfirmPhase = false;
try {
while (rc.attempt()) {
try {
if (!inConfirmPhase) {
// Initial migrate request
service.migrateServer(server.getId());
// Wait for change to verify resize
waitForStateChange(rc, server, Status.READY);
inConfirmPhase = true;
}
// Verify resize
service.processResize(server);
// Wait for complete. will go back to init status
waitForStateChange(rc, server, initialStatus);
logger.info("Completed migrate request successfully");
return;
} catch (ContextConnectionException e) {
msg = getConnectionExceptionMessage(rc, ctx, e);
logger.error(msg, e);
rc.delay();
}
}
} catch (ZoneException e) {
String phase = inConfirmPhase ? "VERIFY MIGRATE" : "REQUEST MIGRATE";
msg = EELFResourceManager.format(Msg.MIGRATE_SERVER_FAILED, server.getName(), server.getId(), phase,
e.getMessage());
generateEvent(rc, false, msg);
logger.error(msg, e);
throw new RequestFailedException("Migrate Server", msg, HttpStatus.METHOD_NOT_ALLOWED_405, server);
}
}
/**
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#rebuildServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server rebuildServer(Map params, SvcLogicContext ctx) throws APPCException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, REBUILD_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:Rebuild");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.REBUILDING_SERVER, appName);
String msg;
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
debugParameters(params);
debugContext(ctx);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
// Manually checking image service until new PAL release
if (hasImageAccess(rc, context)) {
rebuildServer(rc, server);
doSuccess(rc);
} else {
msg = EELFResourceManager.format(Msg.REBUILD_SERVER_FAILED, server.getName(), server.getId(),
"Accessing Image Service Failed");
logger.error(msg);
doFailure(rc, HttpStatus.FORBIDDEN_403, msg);
}
context.close();
}
} catch (ResourceNotFoundException e) {
msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
STOP_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
/**
* This method is used to restart an existing virtual machine given the fully qualified URL of the machine.
*
* The fully qualified URL contains enough information to locate the appropriate server. The URL is of the form
*
* [scheme]://[host[:port]] / [path] / [tenant_id] / servers / [vm_id]
*
Where the various parts of the URL can be parsed and extracted and used to locate the appropriate service
* in the provider service catalog. This then allows us to open a context using the CDP abstraction, obtain the
* server by its UUID, and then perform the restart.
*
*
* @throws UnknownProviderException
* If the provider cannot be found
* @throws IllegalArgumentException
* if the expected argument(s) are not defined or are invalid
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#restartServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server restartServer(Map params, SvcLogicContext ctx)
throws UnknownProviderException, IllegalArgumentException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, RESTART_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:Restart");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.RESTARTING_SERVER, appName);
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
debugParameters(params);
debugContext(ctx);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
restartServer(rc, server);
context.close();
doSuccess(rc);
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
RESTART_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
/* *********************************************************************************/
/* DEVEN PANCHAL: This method is used to check the status of the VM */
/**********************************************************************************/
public Server vmStatuschecker(Map params, SvcLogicContext ctx) throws UnknownProviderException, IllegalArgumentException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, VMSTATUSCHECK_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter: vmstatuscheck");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
debugParameters(params);
debugContext(ctx);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
String statusvm;
switch (server.getStatus()) {
case DELETED:
statusvm = "deleted";
break;
case RUNNING:
statusvm = "running";
break;
case ERROR:
statusvm = "error";
break;
case READY:
statusvm = "ready";
break;
case PAUSED:
statusvm = "paused";
break;
case SUSPENDED:
statusvm = "suspended";
break;
case PENDING:
statusvm = "pending";
break;
default:
statusvm = "default-unknown state-should never occur";
break;
}
String statusofVM = statusvm;
context.close();
SvcLogicContext svcLogic = rc.getSvcLogicContext();
svcLogic.setStatus(OUTCOME_SUCCESS);
svcLogic.setAttribute("org.openecomp.statusofvm", statusofVM);
svcLogic.setAttribute(Constants.STATUS_OF_VM, statusofVM);
svcLogic.setAttribute(Constants.ATTRIBUTE_ERROR_CODE, Integer.toString(HttpStatus.OK_200.getStatusCode()));
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
RESTART_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
/* *********************************************************************************/
/**
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#startServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server startServer(Map params, SvcLogicContext ctx) throws APPCException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, START_SERVICE);
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.RESTARTING_SERVER, appName);
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
debugParameters(params);
debugContext(ctx);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
String providerName = params.get(ProviderAdapter.PROPERTY_PROVIDER_NAME);
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
Context context = null;
try {
context = getContext(rc, vm_url, providerName);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
stopServer(rc, server);
server.refreshStatus();
context.close();
doSuccess(rc);
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
START_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
/**
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#stopServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server stopServer(Map params, SvcLogicContext ctx) throws APPCException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, STOP_SERVICE);
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
logger.info(Msg.STOPPING_SERVER, appName);
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
debugParameters(params);
debugContext(ctx);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
ctx.setAttribute("STOP_STATUS", "SUCCESS");
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
if (server.getStatus().equals(Status.PENDING)) {
throw new RequestFailedException("Server is in pending Status");
}
stopServer(rc, server);
server.refreshStatus();
if (server.getStatus().equals(Status.ERROR)) {
throw new RequestFailedException("Server is in ERROR state after operation");
}
context.close();
doSuccess(rc);
}else{
ctx.setAttribute("STOP_STATUS", "SERVER_NOT_FOUND");
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
ctx.setAttribute("STOP_STATUS", "SERVER_NOT_FOUND");
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
STOP_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
ctx.setAttribute("STOP_STATUS", "ERROR");
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
logger.error(EELFResourceManager.format(Msg.STOP_SERVER_FAILED, appName, "n/a", "n/a", e.getMessage()));
ctx.setAttribute("STOP_STATUS", "ERROR");
doFailure(rc, e.getStatus(), e.getMessage());
}
return server;
}
/**
* This method is used to validate that the parameters contain all required property names, and that the values are
* non-null and non-empty strings. We are still not ensured that the value is valid, but at least it exists.
*
* @param ctx
* The request context object that manages the request
* @param parameters
* The parameters to be checked
* @param propertyNames
* The list of property names that are required to be present.
* @throws RequestFailedException
* If the parameters are not valid
*/
@SuppressWarnings({
"nls", "static-method"
})
private void validateParametersExist(@SuppressWarnings("unused") RequestContext ctx, Map parameters, String... propertyNames)
throws RequestFailedException {
boolean success = true;
StringBuilder msg = new StringBuilder(EELFResourceManager.format(Msg.MISSING_REQUIRED_PROPERTIES, MDC.get(MDC_SERVICE)));
msg.append(NL);
for (String propertyName : propertyNames) {
String value = parameters.get(propertyName);
if (value == null || value.trim().length() == 0) {
success = false;
msg.append(QUOTE);
msg.append(propertyName);
msg.append(QUOTE);
msg.append(SPACE);
}
}
if (!success) {
logger.error(msg.toString());
throw new RequestFailedException("Check Parameters", msg.toString(), HttpStatus.BAD_REQUEST_400, (Server)null);
}
}
/**
* This method is used to create a diagnostic dump of the context for the log
*
* @param context
* The context to be dumped
*/
@SuppressWarnings({
"nls", "static-method"
})
private void debugContext(SvcLogicContext context) {
Set keys = context.getAttributeKeySet();
StringBuilder builder = new StringBuilder();
builder.append("Service Logic Context: Status ");
builder.append(LPAREN);
builder.append(context.getStatus());
builder.append(RPAREN);
builder.append(", Attribute count ");
builder.append(LPAREN);
builder.append(keys == null ? "none" : Integer.toString(keys.size()));
builder.append(RPAREN);
if (keys != null && !keys.isEmpty()) {
builder.append(NL);
for (String key : keys) {
String value = context.getAttribute(key);
builder.append("Attribute ");
builder.append(LPAREN);
builder.append(key);
builder.append(RPAREN);
builder.append(", value ");
builder.append(LPAREN);
builder.append(value == null ? "" : value);
builder.append(RPAREN);
builder.append(NL);
}
}
logger.debug(builder.toString());
}
void validateVMURL(VMURL vm) throws RequestFailedException {
String name = "vm-id";
if (vm == null) {
throw new RequestFailedException(String.format("The value %s cannot be null.", name));
}
// Check that its a good uri
// This will probably never get hit bc of an earlier check while parsing
// the string to a VMURL
try {
//noinspection ResultOfMethodCallIgnored
URI.create(vm.toString());
} catch (Exception e) {
throw new RequestFailedException(
String.format("The value %s is not well formed [%s].", name, vm.toString()));
}
// Check the tenant and vmid segments
String patternRegex = "([0-9a-f]{8}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{12})";
Pattern pattern = Pattern.compile(patternRegex, Pattern.CASE_INSENSITIVE);
if (!pattern.matcher(vm.getTenantId()).matches()) {
throw new RequestFailedException(
String.format("The value %s has an invalid tenantId [%s].", name, vm.getTenantId()));
}
if (!pattern.matcher(vm.getServerId()).matches()) {
throw new RequestFailedException(
String.format("The value %s has an invalid serverId [%s].", name, vm.getServerId()));
}
}
@SuppressWarnings("unused")
private void validateIdentityURL(IdentityURL id) throws RequestFailedException {
String name = "identity-url";
if (id == null) {
throw new RequestFailedException(String.format("The value %s cannot be null.", name));
}
// Check that its a good uri
// This will probably never get hit bc of an earlier check while parsing
// the string to a VMURL
try {
//noinspection ResultOfMethodCallIgnored
URI.create(id.toString());
} catch (Exception e) {
throw new RequestFailedException(
String.format("The value %s is not well formed [%s].", name, id.toString()));
}
}
/**
* This method is used to dump the value of the parameters to the log for debugging purposes.
*
* @param parameters
* The parameters to be printed to the log
*/
@SuppressWarnings("static-method")
private void debugParameters(Map parameters) {
for (String key : parameters.keySet()) {
logger.debug(Msg.PROPERTY_VALUE, key, parameters.get(key));
}
}
/**
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param code
* @param message
*/
@SuppressWarnings("static-method")
private void doFailure(RequestContext rc, HttpStatus code, String message) {
try {
doFailure(rc, code, message, null);
} catch (APPCException ignored) {/* never happens */}
}
private void doFailure(RequestContext rc, HttpStatus code, String message, Throwable cause) throws APPCException {
SvcLogicContext svcLogic = rc.getSvcLogicContext();
String msg = (message == null) ? code.getReasonPhrase() : message;
if (msg.contains("\n")) {
msg = msg.substring(0, msg.indexOf("\n"));
}
String status;
try {
status = Integer.toString(code.getStatusCode());
} catch (Exception e) {
status = "500";
}
svcLogic.setStatus(OUTCOME_FAILURE);
svcLogic.setAttribute(Constants.ATTRIBUTE_ERROR_CODE, status);
svcLogic.setAttribute(Constants.ATTRIBUTE_ERROR_MESSAGE, msg);
svcLogic.setAttribute(Constants.DG_OUTPUT_STATUS_MESSAGE, msg);
if (null != cause) throw new APPCException(cause);
}
/**
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
*/
@SuppressWarnings("static-method")
private void doSuccess(RequestContext rc) {
SvcLogicContext svcLogic = rc.getSvcLogicContext();
svcLogic.setStatus(OUTCOME_SUCCESS);
svcLogic.setAttribute(Constants.ATTRIBUTE_ERROR_CODE, Integer.toString(HttpStatus.OK_200.getStatusCode()));
}
/**
* Generates the event indicating what happened
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param success
* True if the event represents a successful outcome
* @param msg
* The detailed message
*/
private void generateEvent(@SuppressWarnings("unused") RequestContext rc, @SuppressWarnings("unused") boolean success, @SuppressWarnings("unused") String msg) {
// indication to the DG to generate the event?
}
/**
* This method is a general helper method used to locate a server given its fully-qualified self-link URL on a
* supported provider, regardless of region(s), and to return an opened context that can be used to access that
* server.
*
* @param rc
* The request context that wraps and manages the state of the request
* @param selfLinkURL
* The fully-qualified self-link URL of the server
* @param providerName
* The name of the provider to be searched
* @return The context that can be used to access the server, or null if not found.
*/
@SuppressWarnings("nls")
private Context getContext(RequestContext rc, String selfLinkURL, String providerName) {
VMURL vm = VMURL.parseURL(selfLinkURL);
IdentityURL ident = IdentityURL.parseURL(providerName);
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
if (vm == null) {
String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, selfLinkURL);
logger.error(msg);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
return null;
}
/*
* Get the cache of tenants and contexts for the named provider, if one exists
*/
ProviderCache cache = providerCache.get(providerName);
/*
* If one doesn't exist, try and create it. If we have enough information to create it successfully, add it to
* the cache and continue, otherwise fail the request.
*/
if (cache == null) {
if (ident != null) {
cache = createProviderCache(vm, ident);
}
if (cache != null) {
providerCache.put(cache.getProviderName(), cache);
} else {
String msg =
EELFResourceManager.format(Msg.UNKNOWN_PROVIDER, providerName, providerCache.keySet().toString());
logger.error(msg);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
return null;
}
}
if (providerName == null) {
logger
.debug(String.format("Using the default provider cache [%s] since no valid identity url was passed in.",
cache.getIdentityURL()));
}
// get the tenant cache for the vm
String identityURL = cache.getIdentityURL();
TenantCache tenantCache = cache.getTenant(vm.getTenantId());
if(tenantCache == null){
//no tenantCache matching tenant, add tenant to the provider cache
tenantCache = cache.addTenant(vm.getTenantId(),null,DEFAULT_USER, DEFAULT_PASS);
if(tenantCache == null){
//tenant not found
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
return null;
}
}
//reserve the context
String tenantName = tenantCache.getTenantName();
String tenantId = tenantCache.getTenantId();
String region = tenantCache.determineRegion(vm);
if (region != null) {
Pool pool = tenantCache.getPools().get(region);
while (rc.attempt()) {
try {
Context context = pool.reserve();
/*
* Insert logic here to test the context for connectivity because we may have gotten one from
* the pool that was previously created.
*/
if (context.isStale()) {
context.relogin();
}
return context;
} catch (PoolExtensionException e) {
String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, providerName, identityURL,
tenantName, tenantId, e.getMessage(), Long.toString(rc.getRetryDelay()),
Integer.toString(rc.getAttempts()), Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
} catch (Exception e) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, e,
e.getClass().getSimpleName(), "find", selfLinkURL, tenantCache.getTenantName());
logger.error(msg, e);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
return null;
}
}
String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, providerName, identityURL);
logger.error(msg);
doFailure(rc, HttpStatus.BAD_GATEWAY_502, msg);
return null;
}
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
return null;
}
/**
* initialize the provider adapter by building the context cache
*/
private void initialize() {
configuration = ConfigurationFactory.getConfiguration();
/*
* Initialize the provider cache for all defined providers. The definition of the providers uses a structured
* property set, where the names form a hierarchical name space (dotted notation, such as one.two.three). Each
* name in the name space can also be serialized by appending a sequence number. All nodes at the same level
* with the same serial number are grouped together in the namespace hierarchy. This allows a hierarchical
* multi-valued property to be defined, which can then be used to setup the provider and tenant caches.
*
* For example, the following definitions show how the namespace hierarchy is defined for two providers, with
* two tenants on the first provider and a single tenant for the second provider.
* provider1.type=OpenStackProvider1
* provider1.name=OpenStackProviderName1
* provider1.identity=http://192.168.1.2:5000/v2.0
* provider1.tenant1.name=MY-TENANT-NAME
* provider1.tenant1.userid=userid
* provider1.tenant1.password=userid@123
* provider1.tenant2.name=MY-TENANT-NAME
* provider1.tenant2.userid=userid
* provider1.tenant2.password=userid@123
* provider2.type=OpenStackProvider2
* provider2.name=OpenStackProviderName2
* provider2.identity=http://192.168.1.2:5000/v2.0
* provider2.tenant1.name=MY-TENANT-NAME
* provider2.tenant1.userid=userid
* provider2.tenant1.password=userid@123
*
*
*/
providerCache = new HashMap<>();
Properties properties = configuration.getProperties();
List providers = StructuredPropertyHelper.getStructuredProperties(properties, PROPERTY_PROVIDER);
for (Node provider : providers) {
ProviderCache cache = new ProviderCache();
List providerNodes = provider.getChildren();
for (Node node : providerNodes) {
if (node.getName().equals(PROPERTY_PROVIDER_TYPE)) {
cache.setProviderType(node.getValue());
} else if (node.getName().equals(PROPERTY_PROVIDER_IDENTITY)) {
cache.setIdentityURL(node.getValue());
cache.setProviderName(node.getValue());
} else if (node.getName().startsWith(PROPERTY_PROVIDER_TENANT)) {
String tenantName = null;
String userId = null;
String password = null;
for (Node node2 : node.getChildren()) {
switch (node2.getName()) {
case PROPERTY_PROVIDER_TENANT_NAME:
tenantName = node2.getValue();
break;
case PROPERTY_PROVIDER_TENANT_USERID:
userId = node2.getValue();
DEFAULT_USER = node2.getValue();
break;
case PROPERTY_PROVIDER_TENANT_PASSWORD:
password = node2.getValue();
DEFAULT_PASS = node2.getValue();
break;
}
}
cache.addTenant(null, tenantName, userId, password);
}
}
/*
* Add the provider to the set of providers cached
*/
if (cache.getIdentityURL() != null && cache.getProviderType() != null) {
providerCache.put(null, cache);
providerCache.put(cache.getIdentityURL(), cache);
}
/*
* Now, initialize the cache for the loaded provider
*/
cache.initialize();
}
}
/**
* This method is called to rebuild the provided server.
*
* If the server was booted from a volume, then the request is failed immediately and no action is taken. Rebuilding
* a VM from a bootable volume, where the bootable volume itself is not rebuilt, serves no purpose.
*
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* @throws ZoneException
* @throws RequestFailedException
*/
@SuppressWarnings("nls")
private void rebuildServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
ServerBootSource builtFrom = server.getBootSource();
String msg;
// Throw exception for non image/snap boot source
if (ServerBootSource.VOLUME.equals(builtFrom)) {
msg = String.format("Rebuilding is currently not supported for servers built from bootable volumes [%s]",
server.getId());
generateEvent(rc, false, msg);
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.FORBIDDEN_403, server);
}
/*
* Pending is a bit of a special case. If we find the server is in a pending state, then the provider is in the
* process of changing state of the server. So, lets try to wait a little bit and see if the state settles down
* to one we can deal with. If not, then we have to fail the request.
*/
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
if (server.getStatus().equals(Status.PENDING)) {
waitForStateChange(rc, server, Status.READY, Status.RUNNING, Status.ERROR, Status.SUSPENDED, Status.PAUSED);
}
/*
* Get the image to use. This is determined by the presence or absence of snapshot images. If any snapshots
* exist, then the latest snapshot is used, otherwise the image used to construct the VM is used.
*/
List snapshots = server.getSnapshots();
String imageToUse;
if (snapshots != null && !snapshots.isEmpty()) {
imageToUse = snapshots.get(0).getId();
} else {
imageToUse = server.getImage();
ImageService imageService = server.getContext().getImageService();
try {
while (rc.attempt()) {
try {
/*
* We are just trying to make sure that the image exists. We arent interested in the details at
* this point.
*/
imageService.getImage(imageToUse);
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(),
imageService.getURL(), context.getTenant().getName(), context.getTenant().getId(),
e.getMessage(), Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
} catch (ZoneException e) {
msg = EELFResourceManager.format(Msg.IMAGE_NOT_FOUND, imageToUse, "rebuild");
generateEvent(rc, false, msg);
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.METHOD_NOT_ALLOWED_405, server);
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
/*
* We determine what to do based on the current state of the server
*/
switch (server.getStatus()) {
case DELETED:
// Nothing to do, the server is gone
msg = EELFResourceManager.format(Msg.SERVER_DELETED, server.getName(), server.getId(),
server.getTenantId(), "rebuilt");
generateEvent(rc, false, msg);
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.METHOD_NOT_ALLOWED_405, server);
case RUNNING:
// Attempt to stop the server, then rebuild it
stopServer(rc, server);
rebuildServer(rc, server, imageToUse);
startServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case ERROR:
msg = EELFResourceManager.format(Msg.SERVER_ERROR_STATE, server.getName(), server.getId(),
server.getTenantId(), "rebuild");
generateEvent(rc, false, msg);
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.METHOD_NOT_ALLOWED_405, server);
case READY:
// Attempt to rebuild the server
rebuildServer(rc, server, imageToUse);
startServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case PAUSED:
// if paused, un-pause it, stop it, and rebuild it
unpauseServer(rc, server);
stopServer(rc, server);
rebuildServer(rc, server, imageToUse);
startServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case SUSPENDED:
// Attempt to resume the suspended server, stop it, and rebuild it
resumeServer(rc, server);
stopServer(rc, server);
rebuildServer(rc, server, imageToUse);
startServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
default:
// Hmmm, unknown status, should never occur
msg = EELFResourceManager.format(Msg.UNKNOWN_SERVER_STATE, server.getName(), server.getId(),
server.getTenantId(), server.getStatus().name());
generateEvent(rc, false, msg);
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.METHOD_NOT_ALLOWED_405, server);
}
}
/**
* This method handles the case of restarting a server once we have found the server and have obtained the abstract
* representation of the server via the context (i.e., the "Server" object from the CDP-Zones abstraction).
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server object representing the server we want to operate on
* @throws ZoneException
*/
@SuppressWarnings("nls")
private void restartServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
/*
* Pending is a bit of a special case. If we find the server is in a pending state, then the provider is in the
* process of changing state of the server. So, lets try to wait a little bit and see if the state settles down
* to one we can deal with. If not, then we have to fail the request.
*/
String msg;
if (server.getStatus().equals(Status.PENDING)) {
waitForStateChange(rc, server, Status.READY, Status.RUNNING, Status.ERROR, Status.SUSPENDED, Status.PAUSED);
}
/*
* We determine what to do based on the current state of the server
*/
switch (server.getStatus()) {
case DELETED:
// Nothing to do, the server is gone
msg = EELFResourceManager.format(Msg.SERVER_DELETED, server.getName(), server.getId(),
server.getTenantId(), "restarted");
generateEvent(rc, false, msg);
logger.error(msg);
break;
case RUNNING:
// Attempt to stop and start the server
stopServer(rc, server);
startServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case ERROR:
msg = EELFResourceManager.format(Msg.SERVER_ERROR_STATE, server.getName(), server.getId(),
server.getTenantId(), "rebuild");
generateEvent(rc, false, msg);
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.METHOD_NOT_ALLOWED_405, server);
case READY:
// Attempt to start the server
startServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case PAUSED:
// if paused, un-pause it
unpauseServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case SUSPENDED:
// Attempt to resume the suspended server
resumeServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
default:
// Hmmm, unknown status, should never occur
msg = EELFResourceManager.format(Msg.UNKNOWN_SERVER_STATE, server.getName(), server.getId(),
server.getTenantId(), server.getStatus().name());
generateEvent(rc, false, msg);
logger.error(msg);
break;
}
}
/**
* Resume a suspended server and wait for it to enter a running state
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server to be resumed
* @throws ZoneException
* @throws RequestFailedException
*/
@SuppressWarnings("nls")
private void resumeServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
logger.debug(Msg.RESUME_SERVER, server.getId());
Context context = server.getContext();
String msg;
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
while (rc.attempt()) {
try {
server.resume();
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Resume Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
waitForStateChange(rc, server, Status.RUNNING);
}
/**
* Start the server and wait for it to enter a running state
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server to be started
* @throws ZoneException
* @throws RequestFailedException
*/
@SuppressWarnings("nls")
private void startServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
logger.debug(Msg.START_SERVER, server.getId());
String msg;
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
while (rc.attempt()) {
try {
server.start();
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Start Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
waitForStateChange(rc, server, Status.RUNNING);
}
/**
* Stop the specified server and wait for it to stop
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server to be stopped
* @throws ZoneException
* @throws RequestFailedException
*/
@SuppressWarnings("nls")
private void stopServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
logger.debug(Msg.STOP_SERVER, server.getId());
String msg;
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
while (rc.attempt()) {
try {
server.stop();
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Stop Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
waitForStateChange(rc, server, Status.READY, Status.ERROR);
}
/**
* Un-Pause a paused server and wait for it to enter a running state
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server to be un-paused
* @throws ZoneException
* @throws RequestFailedException
*/
@SuppressWarnings("nls")
private void unpauseServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
logger.debug(Msg.UNPAUSE_SERVER, server.getId());
String msg;
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
while (rc.attempt()) {
try {
server.unpause();
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Unpause Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
waitForStateChange(rc, server, Status.RUNNING, Status.READY);
}
/**
* Enter a pool-wait loop checking the server state to see if it has entered one of the desired states or not.
*
* This method checks the state of the server periodically for one of the desired states. When the server enters one
* of the desired states, the method returns a successful indication (true). If the server never enters one of the
* desired states within the allocated timeout period, then the method returns a failed response (false). No
* exceptions are thrown from this method.
*
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server to wait on
* @param desiredStates
* A variable list of desired states, any one of which is allowed.
* @throws RequestFailedException
* If the request times out or fails for some reason
*/
@SuppressWarnings("nls")
private void waitForStateChange(RequestContext rc, Server server, Server.Status... desiredStates)
throws RequestFailedException {
int pollInterval = configuration.getIntegerProperty(Constants.PROPERTY_OPENSTACK_POLL_INTERVAL);
int timeout = configuration.getIntegerProperty(Constants.PROPERTY_SERVER_STATE_CHANGE_TIMEOUT);
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
String msg;
long endTime = System.currentTimeMillis() + (timeout * 1000); //
while (rc.attempt()) {
try {
try {
server.waitForStateChange(pollInterval, timeout, desiredStates);
break;
} catch (TimeoutException e) {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
List list = new ArrayList<>();
for (Server.Status desiredState : desiredStates) {
list.add(desiredState.name());
}
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
} catch (ZoneException e) {
List list = new ArrayList<>();
for (Server.Status desiredState : desiredStates) {
list.add(desiredState.name());
}
String reason = EELFResourceManager.format(Msg.STATE_CHANGE_EXCEPTION, e.getClass().getSimpleName(),
"server", server.getName(), server.getId(), StringHelper.asList(list), server.getStatus().name(),
e.getMessage());
logger.error(reason);
logger.error(EELFResourceManager.format(e));
// Instead of failing we are going to wait and try again.
// Timeout is reduced by delay time
logger.info(String.format("Retrying in %ds", rc.getRetryDelay()));
rc.delay();
timeout = (int) (endTime - System.currentTimeMillis()) / 1000;
// throw new RequestFailedException(e, operation, reason,
// HttpStatus.BAD_GATEWAY_502, server);
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Waiting for State Change", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
}
/**
* Enter a pool-wait loop checking the server state to see if it has entered one of the desired states or not.
*
* This method checks the state of the server periodically for one of the desired states. When the server enters one
* of the desired states, the method returns a successful indication (true). If the server never enters one of the
* desired states within the allocated timeout period, then the method returns a failed response (false). No
* exceptions are thrown from this method.
*
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param image
* The server to wait on
* @param desiredStates
* A variable list of desired states, any one of which is allowed.
* @throws RequestFailedException
* If the request times out or fails for some reason
* @throws NotLoggedInException
*/
@SuppressWarnings("nls")
private void waitForStateChange(RequestContext rc, Image image, Image.Status... desiredStates)
throws RequestFailedException, NotLoggedInException {
int pollInterval = configuration.getIntegerProperty(Constants.PROPERTY_OPENSTACK_POLL_INTERVAL);
int timeout = configuration.getIntegerProperty(Constants.PROPERTY_SERVER_STATE_CHANGE_TIMEOUT);
Context context = image.getContext();
Provider provider = context.getProvider();
ImageService service = context.getImageService();
String msg;
long endTime = System.currentTimeMillis() + (timeout * 1000); //
while (rc.attempt()) {
try {
try {
image.waitForStateChange(pollInterval, timeout, desiredStates);
break;
} catch (TimeoutException e) {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
List list = new ArrayList<>();
for (Image.Status desiredState : desiredStates) {
list.add(desiredState.name());
}
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
} catch (ZoneException e) {
List list = new ArrayList<>();
for (Image.Status desiredState : desiredStates) {
list.add(desiredState.name());
}
String reason = EELFResourceManager.format(Msg.STATE_CHANGE_EXCEPTION, e.getClass().getSimpleName(),
"server", image.getName(), image.getId(), StringHelper.asList(list), image.getStatus().name(),
e.getMessage());
logger.error(reason);
logger.error(EELFResourceManager.format(e));
// Instead of failing we are going to wait and try again.
// Timeout is reduced by delay time
logger.info(String.format("Retrying in %ds", rc.getRetryDelay()));
rc.delay();
timeout = (int) (endTime - System.currentTimeMillis()) / 1000;
// throw new RequestFailedException(e, operation, reason,
// HttpStatus.BAD_GATEWAY_502, server);
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Waiting for State Change", msg, HttpStatus.BAD_GATEWAY_502, new Server());
}
rc.reset();
}
/**
* Rebuild the indicated server with the indicated image. This method assumes the server has been determined to be
* in the correct state to do the rebuild.
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* the server to be rebuilt
* @param image
* The image to be used (or snapshot)
* @throws RequestFailedException
* if the server does not change state in the allotted time
*/
@SuppressWarnings("nls")
private void rebuildServer(RequestContext rc, Server server, String image) throws RequestFailedException {
String msg;
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
try {
while (rc.attempt()) {
try {
server.rebuild(image);
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
/*
* We need to provide some time for OpenStack to start processing the request.
*/
try {
Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
logger.trace("Sleep threw interrupted exception, should never occur");
}
} catch (ZoneException e) {
msg =
EELFResourceManager.format(Msg.REBUILD_SERVER_FAILED, server.getName(), server.getId(), e.getMessage());
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
/*
* Once we have started the process, now we wait for the final state of stopped. This should be the final state
* (since we started the rebuild with the server stopped).
*/
waitForStateChange(rc, server, Status.READY);
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Rebuild Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
}
/**
* Looks up the indicated server using the provided context and returns the server to the caller
*
* @param rc
* The request context
* @param context
* The provider context
* @param id
* The id of the server
* @return The server, or null if there is a problem
* @throws ZoneException
* If the server cannot be found
* @throws RequestFailedException
* If the server cannot be found because we cant connect to the provider
*/
@SuppressWarnings("nls")
private Server lookupServer(RequestContext rc, Context context, String id)
throws ZoneException, RequestFailedException {
ComputeService service = context.getComputeService();
Server server = null;
String msg;
Provider provider = context.getProvider();
while (rc.attempt()) {
try {
server = service.getServer(id);
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
doFailure(rc, HttpStatus.BAD_GATEWAY_502, msg);
throw new RequestFailedException("Lookup Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
return server;
}
private String getConnectionExceptionMessage(RequestContext rc, Context ctx, ContextConnectionException e)
throws ZoneException {
return EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, ctx.getProvider().getName(),
ctx.getComputeService().getURL(), ctx.getTenant().getName(), ctx.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
}
private ProviderCache createProviderCache(VMURL vm, IdentityURL ident) {
if (vm != null && ident != null) {
ProviderCache cache = new ProviderCache();
cache.setIdentityURL(ident.toString());
cache.setProviderName(ident.toString());
// cache.setProviderType("OpenStack");
TenantCache tenant = cache.addTenant(vm.getTenantId(),null, DEFAULT_USER, DEFAULT_PASS);
// Make sure we could initialize the the cache otherwise return null
if (tenant != null && tenant.isInitialized()) {
return cache;
}
}
return null;
}
/**
* This method is used to delete an existing virtual machine given the fully qualified URL of the machine.
*
* The fully qualified URL contains enough information to locate the appropriate server. The URL is of the form
*
* [scheme]://[host[:port]] / [path] / [tenant_id] / servers / [vm_id]
*
Where the various parts of the URL can be parsed and extracted and used to locate the appropriate service
* in the provider service catalog. This then allows us to open a context using the CDP abstraction, obtain the
* server by its UUID, and then perform the restart.
*
*
* @throws UnknownProviderException
* If the provider cannot be found
* @throws IllegalArgumentException
* if the expected argument(s) are not defined or are invalid
* @see org.openecomp.appc.adapter.iaas.ProviderAdapter#terminateServer(java.util.Map, org.openecomp.sdnc.sli.SvcLogicContext)
*/
@SuppressWarnings("nls")
@Override
public Server terminateServer(Map params, SvcLogicContext ctx)
throws UnknownProviderException, IllegalArgumentException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, TERMINATE_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:Terminate");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
if (logger.isDebugEnabled()) {
logger.debug("Inside org.openecomp.appc.adapter.iaas.impl.ProviderAdapter.terminateServer");
}
try {
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
debugParameters(params);
debugContext(ctx);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
ctx.setAttribute("TERMINATE_STATUS", "SUCCESS");
VMURL vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
Context context = null;
try {
context = getContext(rc, vm_url, identStr);
if (context != null) {
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
logger.info(EELFResourceManager.format(Msg.TERMINATING_SERVER, server.getName()));
terminateServer(rc, server);
logger.info(EELFResourceManager.format(Msg.TERMINATE_SERVER, server.getName()));
context.close();
doSuccess(rc);
}else{
ctx.setAttribute("TERMINATE_STATUS", "SERVER_NOT_FOUND");
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
ctx.setAttribute("TERMINATE_STATUS", "SERVER_NOT_FOUND");
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
RESTART_SERVICE, vm_url, context == null ? "Unknown" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
logger.error(EELFResourceManager.format(Msg.TERMINATE_SERVER_FAILED, appName, "n/a", "n/a", e.getMessage()));
doFailure(rc, e.getStatus(), e.getMessage());
ctx.setAttribute("TERMINATE_STATUS", "ERROR");
}
return server;
}
/**
* This method handles the case of restarting a server once we have found the server and have obtained the abstract
* representation of the server via the context (i.e., the "Server" object from the CDP-Zones abstraction).
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server object representing the server we want to operate on
* @throws ZoneException
*/
@SuppressWarnings("nls")
private void terminateServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
/*
* Pending is a bit of a special case. If we find the server is in a pending state, then the provider is in the
* process of changing state of the server. So, lets try to wait a little bit and see if the state settles down
* to one we can deal with. If not, then we have to fail the request.
*/
String msg;
if (server.getStatus().equals(Status.PENDING)) {
waitForStateChange(rc, server, Status.READY, Status.RUNNING, Status.ERROR, Status.SUSPENDED, Status.PAUSED);
}
/*
* We determine what to do based on the current state of the server
*/
switch (server.getStatus()) {
case DELETED:
// Nothing to do, the server is gone
msg = EELFResourceManager.format(Msg.SERVER_DELETED, server.getName(), server.getId(),
server.getTenantId(), "restarted");
generateEvent(rc, false, msg);
logger.error(msg);
break;
case RUNNING:
// Attempt to stop and start the server
logger.info("stopping SERVER");
stopServer(rc, server);
deleteServer(rc, server);
logger.info("after delete SERVER");
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
case ERROR:
case READY:
case PAUSED:
case SUSPENDED:
// Attempt to delete the suspended server
deleteServer(rc, server);
generateEvent(rc, true, OUTCOME_SUCCESS);
break;
default:
// Hmmm, unknown status, should never occur
msg = EELFResourceManager.format(Msg.UNKNOWN_SERVER_STATE, server.getName(), server.getId(),
server.getTenantId(), server.getStatus().name());
generateEvent(rc, false, msg);
logger.error(msg);
break;
}
}
/**
* Start the server and wait for it to enter a running state
*
* @param rc
* The request context that manages the state and recovery of the request for the life of its processing.
* @param server
* The server to be started
* @throws ZoneException
* @throws RequestFailedException
*/
@SuppressWarnings("nls")
private void deleteServer(RequestContext rc, Server server) throws ZoneException, RequestFailedException {
String msg;
Context context = server.getContext();
Provider provider = context.getProvider();
ComputeService service = context.getComputeService();
while (rc.attempt()) {
try {
logger.info("deleting SERVER");
server.delete();
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), service.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
logger.error(msg);
throw new RequestFailedException("Delete Server", msg, HttpStatus.BAD_GATEWAY_502, server);
}
rc.reset();
}
private boolean hasImageAccess(@SuppressWarnings("unused") RequestContext rc, Context context) {
logger.info("Checking permissions for image service.");
try {
ImageService service = context.getImageService();
service.getImageByName("CHECK_IMAGE_ACCESS");
logger.info("Image service is accessible.");
return true;
} catch (ZoneException e) {
logger.warn("Image service could not be accessed. Some operations may fail.", e);
return false;
}
}
@SuppressWarnings("nls")
@Override
public Stack terminateStack(Map params, SvcLogicContext ctx) throws IllegalArgumentException, APPCException {
Stack stack = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
ctx.setAttribute("TERMINATE_STATUS", "STACK_NOT_FOUND");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
try {
logAndValidate(params, ctx, rc, TERMINATE_STACK, "Terminate Stack",
ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME,
ProviderAdapter.PROPERTY_STACK_ID);
String stackId = params.get(ProviderAdapter.PROPERTY_STACK_ID);
String vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
Context context = resolveContext(rc, params, appName, vm_url);
try {
if (context != null) {
stack = lookupStack(rc, context, stackId);
logger.debug(Msg.STACK_FOUND, vm_url, context.getTenantName(), stack.getStatus().toString());
logger.info(EELFResourceManager.format(Msg.TERMINATING_STACK, stack.getName()));
deleteStack(rc, stack);
logger.info(EELFResourceManager.format(Msg.TERMINATE_STACK, stack.getName()));
context.close();
doSuccess(rc);
}else{
ctx.setAttribute("TERMINATE_STATUS", "SERVER_NOT_FOUND");
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.STACK_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.STACK_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
TERMINATE_STACK, vm_url, context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
logger.error(EELFResourceManager.format(Msg.TERMINATE_STACK_FAILED, appName, "n/a", "n/a"));
doFailure(rc, e.getStatus(), e.getMessage());
}
return stack;
}
@Override
public Stack snapshotStack(Map params, SvcLogicContext ctx) throws IllegalArgumentException, APPCException {
Stack stack = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
ctx.setAttribute("SNAPSHOT_STATUS", "STACK_NOT_FOUND");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
String vm_url = null;
Context context = null;
try {
logAndValidate(params, ctx, rc, SNAPSHOT_STACK, "Snapshot Stack",
ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME,
ProviderAdapter.PROPERTY_STACK_ID);
String stackId = params.get(ProviderAdapter.PROPERTY_STACK_ID);
vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
context = resolveContext(rc, params, appName, vm_url);
if (context != null) {
stack = lookupStack(rc, context, stackId);
logger.debug(Msg.STACK_FOUND, vm_url, context.getTenantName(), stack.getStatus().toString());
logger.info(EELFResourceManager.format(Msg.SNAPSHOTING_STACK, stack.getName()));
Snapshot snapshot = snapshotStack(rc, stack);
ctx.setAttribute(ProviderAdapter.DG_OUTPUT_PARAM_NAMESPACE +
ProviderAdapter.PROPERTY_SNAPSHOT_ID, snapshot.getId());
logger.info(EELFResourceManager.format(Msg.STACK_SNAPSHOTED, stack.getName(), snapshot.getId()));
context.close();
doSuccess(rc);
} else {
ctx.setAttribute(Constants.DG_ATTRIBUTE_STATUS, "failure");
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.STACK_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg, e);
} catch (RequestFailedException e) {
logger.error(EELFResourceManager.format(Msg.MISSING_PARAMETER_IN_REQUEST, e.getReason(), "snapshotStack"));
doFailure(rc, e.getStatus(), e.getMessage(), e);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.STACK_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
"snapshotStack", vm_url, null == context ? "n/a" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg, t);
}
return stack;
}
@Override
public Stack restoreStack(Map params, SvcLogicContext ctx) throws IllegalArgumentException, APPCException {
Stack stack = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive();
ctx.setAttribute("SNAPSHOT_STATUS", "STACK_NOT_FOUND");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
String vm_url = null;
Context context = null;
try {
logAndValidate(params, ctx, rc, SNAPSHOT_STACK, "Snapshot Stack",
ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME,
ProviderAdapter.PROPERTY_STACK_ID,
ProviderAdapter.PROPERTY_INPUT_SNAPSHOT_ID);
String stackId = params.get(ProviderAdapter.PROPERTY_STACK_ID);
vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
String snapshotId = params.get(ProviderAdapter.PROPERTY_INPUT_SNAPSHOT_ID);
context = resolveContext(rc, params, appName, vm_url);
if (context != null) {
stack = lookupStack(rc, context, stackId);
logger.debug(Msg.STACK_FOUND, vm_url, context.getTenantName(), stack.getStatus().toString());
logger.info(EELFResourceManager.format(Msg.RESTORING_STACK, stack.getName(), snapshotId));
restoreStack(stack, snapshotId);
logger.info(EELFResourceManager.format(Msg.STACK_RESTORED, stack.getName(), snapshotId));
context.close();
doSuccess(rc);
} else {
ctx.setAttribute(Constants.DG_ATTRIBUTE_STATUS, "failure");
}
} catch (ResourceNotFoundException e) {
String msg = EELFResourceManager.format(Msg.STACK_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg, e);
} catch (RequestFailedException e) {
logger.error(EELFResourceManager.format(Msg.MISSING_PARAMETER_IN_REQUEST, e.getReason(), "restoreStack"));
doFailure(rc, e.getStatus(), e.getMessage(), e);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.STACK_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
"restoreStack", vm_url, null == context ? "n/a" : context.getTenantName());
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg, t);
}
return stack;
}
private void logAndValidate(Map params, SvcLogicContext ctx, RequestContext rc, String methodName, String serviceName, String ... attributes)
throws RequestFailedException {
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, SNAPSHOT_STACK);
MDC.put(MDC_SERVICE_NAME, String.format("App-C IaaS Adapter:%s", serviceName));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Inside org.openecomp.appc.adapter.iaas.impl.ProviderAdapter.%s", methodName));
}
validateParametersExist(rc, params, attributes);
debugParameters(params);
debugContext(ctx);
}
private Context resolveContext(RequestContext rc, Map params, String appName, String vm_url)
throws RequestFailedException {
VMURL vm = VMURL.parseURL(vm_url);
if (vm == null) {
String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vm_url);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
logger.error(msg);
return null;
}
validateVMURL(vm);
IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
String identStr = (ident == null) ? null : ident.toString();
return getContext(rc, vm_url, identStr);
}
private void deleteStack(RequestContext rc, Stack stack) throws ZoneException, RequestFailedException {
SvcLogicContext ctx = rc.getSvcLogicContext();
Context context = stack.getContext();
StackService stackService = context.getStackService();
logger.debug("Deleting Stack: " + "id:{ " + stack.getId() + "}");
stackService.deleteStack(stack);
// wait for the stack deletion
boolean success = waitForStackStatus(rc, stack, Stack.Status.DELETED);
if (success) {
ctx.setAttribute("TERMINATE_STATUS", "SUCCESS");
} else {
ctx.setAttribute("TERMINATE_STATUS", "ERROR");
throw new RequestFailedException("Delete Stack failure : " + Msg.STACK_OPERATION_EXCEPTION.toString());
}
}
private boolean waitForStackStatus(RequestContext rc, Stack stack, Stack.Status expectedStatus) throws ZoneException, RequestFailedException {
SvcLogicContext ctx = rc.getSvcLogicContext();
Context context = stack.getContext();
StackService stackService = context.getStackService();
int pollInterval = configuration.getIntegerProperty(Constants.PROPERTY_OPENSTACK_POLL_INTERVAL);
int timeout = configuration.getIntegerProperty(Constants.PROPERTY_STACK_STATE_CHANGE_TIMEOUT);
long maxTimeToWait = System.currentTimeMillis() + (long) timeout * 1000;
Stack.Status stackStatus;
while (System.currentTimeMillis() < maxTimeToWait) {
stackStatus = stackService.getStack(stack.getName(), stack.getId()).getStatus();
logger.debug("Stack status : " + stackStatus.toString());
if (stackStatus == expectedStatus) {
return true;
} else if (stackStatus == Stack.Status.FAILED) {
return false;
} else {
try {
Thread.sleep(pollInterval * 1000);
} catch (InterruptedException e) {
logger.trace("Sleep threw interrupted exception, should never occur");
}
}
}
ctx.setAttribute("TERMINATE_STATUS", "ERROR");
throw new TimeoutException("Timeout waiting for stack status change");
}
private Snapshot snapshotStack(@SuppressWarnings("unused") RequestContext rc, Stack stack) throws ZoneException, RequestFailedException {
Snapshot snapshot = new Snapshot();
Context context = stack.getContext();
OpenStackContext osContext = (OpenStackContext)context;
final HeatConnector heatConnector = osContext.getHeatConnector();
((OpenStackContext)context).refreshIfStale(heatConnector);
trackRequest(context);
RequestState.put("SERVICE", "Orchestration");
RequestState.put("SERVICE_URL", heatConnector.getEndpoint());
Heat heat = heatConnector.getClient();
SnapshotResource snapshotResource = new SnapshotResource(heat);
try {
snapshot = snapshotResource.create(stack.getName(), stack.getId(), new CreateSnapshotParams()).execute();
// wait for the stack deletion
StackResource stackResource = new StackResource(heat);
if (!waitForStack(stack, stackResource, "SNAPSHOT_COMPLETE")) {
throw new RequestFailedException("Stack Snapshot failed.");
}
} catch (OpenStackBaseException e) {
ExceptionMapper.mapException(e);
}
return snapshot;
}
private void restoreStack(Stack stack, String snapshotId) throws ZoneException, RequestFailedException {
Context context = stack.getContext();
OpenStackContext osContext = (OpenStackContext)context;
final HeatConnector heatConnector = osContext.getHeatConnector();
((OpenStackContext)context).refreshIfStale(heatConnector);
trackRequest(context);
RequestState.put("SERVICE", "Orchestration");
RequestState.put("SERVICE_URL", heatConnector.getEndpoint());
Heat heat = heatConnector.getClient();
SnapshotResource snapshotResource = new SnapshotResource(heat);
try {
snapshotResource.restore(stack.getName(), stack.getId(), snapshotId).execute();
// wait for the snapshot restore
StackResource stackResource = new StackResource(heat);
if (!waitForStack(stack, stackResource, "RESTORE_COMPLETE")) {
throw new RequestFailedException("Snapshot restore failed.");
}
} catch (OpenStackBaseException e) {
ExceptionMapper.mapException(e);
}
}
private boolean waitForStack(Stack stack, StackResource stackResource, String expectedStatus)
throws OpenStackBaseException, TimeoutException {
int pollInterval = configuration.getIntegerProperty(Constants.PROPERTY_OPENSTACK_POLL_INTERVAL);
int timeout = configuration.getIntegerProperty(Constants.PROPERTY_STACK_STATE_CHANGE_TIMEOUT);
long maxTimeToWait = System.currentTimeMillis() + (long) timeout * 1000;
while (System.currentTimeMillis() < maxTimeToWait) {
String stackStatus = stackResource.show(stack.getName(), stack.getId()).execute().getStackStatus();
logger.debug("Stack status : " + stackStatus);
if (stackStatus.toUpperCase().contains("FAILED")) return false;
if(checkStatus(expectedStatus, pollInterval, stackStatus)) return true;
}
throw new TimeoutException("Timeout waiting for stack status change");
}
private boolean checkStatus(String expectedStatus, int pollInterval, String actualStatus) {
if (actualStatus.toUpperCase().equals(expectedStatus)) {
return true;
} else {
try {
Thread.sleep(pollInterval * 1000);
} catch (InterruptedException ignored) {
}
}
return false;
}
private void trackRequest(Context context, AbstractService.State... states) {
RequestState.clear();
if (null == states) return;
for (AbstractService.State state : states) {
RequestState.put(state.getName(), state.getValue());
}
Thread currentThread = Thread.currentThread();
StackTraceElement[] stack = currentThread.getStackTrace();
if (stack != null && stack.length > 0) {
int index = 0;
StackTraceElement element;
for (; index < stack.length; index++) {
element = stack[index];
if ("trackRequest".equals(element.getMethodName())) { //$NON-NLS-1$
break;
}
}
index++;
if (index < stack.length) {
element = stack[index];
RequestState.put(RequestState.METHOD, element.getMethodName());
RequestState.put(RequestState.CLASS, element.getClassName());
RequestState.put(RequestState.LINE_NUMBER, Integer.toString(element.getLineNumber()));
RequestState.put(RequestState.THREAD, currentThread.getName());
RequestState.put(RequestState.PROVIDER, context.getProvider().getName());
RequestState.put(RequestState.TENANT, context.getTenantName());
RequestState.put(RequestState.PRINCIPAL, context.getPrincipal());
}
}
}
private Stack lookupStack(RequestContext rc, Context context, String id)
throws ZoneException, RequestFailedException {
StackService stackService = context.getStackService();
Stack stack = null;
String msg;
Provider provider = context.getProvider();
while (rc.attempt()) {
try {
List stackList = stackService.getStacks();
for (Stack stackObj : stackList) {
if (stackObj.getId().equals(id)) {
stack = stackObj;
break;
}
}
break;
} catch (ContextConnectionException e) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), stackService.getURL(),
context.getTenant().getName(), context.getTenant().getId(), e.getMessage(),
Long.toString(rc.getRetryDelay()), Integer.toString(rc.getAttempts()),
Integer.toString(rc.getRetryLimit()));
logger.error(msg, e);
rc.delay();
}
}
if (rc.isFailed()) {
msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), stackService.getURL());
logger.error(msg);
doFailure(rc, HttpStatus.BAD_GATEWAY_502, msg);
throw new RequestFailedException("Lookup Stack", msg, HttpStatus.BAD_GATEWAY_502, stack);
}
if (stack == null) {
throw new ResourceNotFoundException("Stack not found with Id : {" + id + "}");
}
return stack;
}
@SuppressWarnings("nls")
@Override
public Server lookupServer(Map params, SvcLogicContext ctx) throws APPCException {
Server server = null;
RequestContext rc = new RequestContext(ctx);
rc.isAlive(); //should we test the return and fail if false?
MDC.put(MDC_ADAPTER, ADAPTER_NAME);
MDC.put(MDC_SERVICE, LOOKUP_SERVICE);
MDC.put(MDC_SERVICE_NAME, "App-C IaaS Adapter:LookupServer");
String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
//for debugging merge into single method?
debugParameters(params);
debugContext(ctx);
String vm_url = null;
VMURL vm = null;
try {
//process vm_url
validateParametersExist(rc, params, ProviderAdapter.PROPERTY_INSTANCE_URL,
ProviderAdapter.PROPERTY_PROVIDER_NAME);
vm_url = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
vm = VMURL.parseURL(vm_url);
if (validateVM(rc, appName, vm_url, vm)) return null;
//use try with resource to ensure context is closed (returned to pool)
try(Context context = resolveContext(rc, params, appName, vm_url)){
//resloveContext & getContext call doFailure and log errors before returning null
if (context != null){
server = lookupServer(rc, context, vm.getServerId());
logger.debug(Msg.SERVER_FOUND, vm_url, context.getTenantName(), server.getStatus().toString());
ctx.setAttribute("serverFound", "success");
doSuccess(rc);
}
} catch (ZoneException e) {
//server not found
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
ctx.setAttribute("serverFound", "failure");
} catch (IOException e) {
//exception closing context
String msg = EELFResourceManager.format(Msg.CLOSE_CONTEXT_FAILED, e, vm_url);
logger.error(msg);
} catch (Throwable t) {
String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, t, t.getClass().getSimpleName(),
LOOKUP_SERVICE, vm_url, "Unknown" );
logger.error(msg, t);
doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
}
} catch (RequestFailedException e) {
// parameters not valid, unable to connect to provider
String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vm_url);
logger.error(msg);
doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
ctx.setAttribute("serverFound", "failure");
}
return server;
}
}