2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
25 package org.onap.appc.adapter.iaas.provider.operation.impl;
27 import org.onap.appc.Constants;
28 import org.onap.appc.adapter.iaas.ProviderAdapter;
29 import org.onap.appc.adapter.iaas.impl.IdentityURL;
30 import org.onap.appc.adapter.iaas.impl.ProviderAdapterImpl;
31 import org.onap.appc.adapter.iaas.impl.RequestContext;
32 import org.onap.appc.adapter.iaas.impl.RequestFailedException;
33 import org.onap.appc.adapter.iaas.impl.VMURL;
34 import org.onap.appc.adapter.iaas.provider.operation.common.enums.Operation;
35 import org.onap.appc.adapter.iaas.provider.operation.impl.base.ProviderServerOperation;
36 import org.onap.appc.configuration.Configuration;
37 import org.onap.appc.configuration.ConfigurationFactory;
38 import org.onap.appc.exceptions.APPCException;
39 import org.onap.appc.i18n.Msg;
40 import com.att.cdp.exceptions.ContextConnectionException;
41 import com.att.cdp.exceptions.ResourceNotFoundException;
42 import com.att.cdp.exceptions.ZoneException;
43 import com.att.cdp.zones.ComputeService;
44 import com.att.cdp.zones.Context;
45 import com.att.cdp.zones.Provider;
46 import com.att.cdp.zones.model.Hypervisor;
47 import com.att.cdp.zones.model.Hypervisor.Status;
48 import com.att.cdp.zones.model.Hypervisor.State;
49 import com.att.cdp.zones.model.Image;
50 import com.att.cdp.zones.model.ModelObject;
51 import com.att.cdp.zones.model.Server;
52 import com.att.eelf.configuration.EELFLogger;
53 import com.att.eelf.configuration.EELFManager;
54 import com.att.eelf.i18n.EELFResourceManager;
55 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
56 import org.glassfish.grizzly.http.util.HttpStatus;
59 import java.io.IOException;
60 import java.text.DateFormat;
61 import java.text.SimpleDateFormat;
62 import java.util.Arrays;
63 import java.util.Date;
64 import java.util.List;
66 import java.util.TimeZone;
67 import static org.onap.appc.adapter.utils.Constants.ADAPTER_NAME;
69 public class EvacuateServer extends ProviderServerOperation {
71 private static final String EVACUATE_STATUS = "EVACUATE_STATUS";
72 private static final String EVACUATE_SERVER = "Evacuate Server";
74 private static final EELFLogger logger = EELFManager.getInstance().getLogger(EvacuateServer.class);
75 private static EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger();
77 private static final Configuration configuration = ConfigurationFactory.getConfiguration();
78 private ProviderAdapterImpl paImpl = null;
80 private void evacuateServer(RequestContext rc, @SuppressWarnings("unused") Server server, String targetHost)
81 throws ZoneException, RequestFailedException {
83 Context ctx = server.getContext();
84 Provider provider = ctx.getProvider();
85 ComputeService service = ctx.getComputeService();
88 * Pending is a bit of a special case. If we find the server is in a pending state, then the provider is in the
89 * process of changing state of the server. So, lets try to wait a little bit and see if the state settles down
90 * to one we can deal with. If not, then we have to fail the request.
93 if (server.getStatus().equals(Server.Status.PENDING)) {
94 waitForStateChange(rc, server, Server.Status.READY, Server.Status.RUNNING, Server.Status.ERROR,
95 Server.Status.SUSPENDED, Server.Status.PAUSED);
97 } catch (RequestFailedException e) {
98 // evacuate is a special case. If the server is still in a Pending state, we want to
99 // continue with evacuate
100 logger.info("Evacuate server - ignore RequestFailedException from waitForStateChange() ..." ,e);
103 setTimeForMetricsLogger();
107 evacuateServerNested(rc,service,server,provider,targetHost);
108 } catch (ZoneException e) {
109 msg = EELFResourceManager.format(Msg.EVACUATE_SERVER_FAILED, server.getName(), server.getId(),
111 logger.error(msg, e);
112 metricsLogger.error(msg);
113 throw new RequestFailedException(EVACUATE_SERVER, msg, HttpStatus.BAD_GATEWAY_502, server);
117 msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, provider.getName(), service.getURL());
119 metricsLogger.error(msg);
120 throw new RequestFailedException(EVACUATE_SERVER, msg, HttpStatus.BAD_GATEWAY_502, server);
125 private void evacuateServerNested(RequestContext rcCtx,ComputeService svc,Server server,Provider provider, String targetHost)
126 throws ZoneException, RequestFailedException {
128 Context ctx = server.getContext();
130 while (rcCtx.attempt()) {
132 logger.debug("Calling CDP moveServer - server id = " + server.getId());
133 svc.moveServer(server.getId(), targetHost);
134 // Wait for completion, expecting the server to go to a non pending state
135 waitForStateChange(rcCtx, server, Server.Status.READY, Server.Status.RUNNING, Server.Status.ERROR,
136 Server.Status.SUSPENDED, Server.Status.PAUSED);
138 } catch (ContextConnectionException e) {
139 msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, provider.getName(), svc.getURL(),
140 ctx.getTenant().getName(), ctx.getTenant().getId(), e.getMessage(),
141 Long.toString(rcCtx.getRetryDelay()), Integer.toString(rcCtx.getAttempts()),
142 Integer.toString(rcCtx.getRetryLimit()));
143 logger.error(msg, e);
144 metricsLogger.error(msg, e);
150 * @see org.onap.appc.adapter.iaas.ProviderAdapter#evacuateServer(java.util.Map,
151 * org.onap.ccsdk.sli.core.sli.SvcLogicContext)
153 private Server evacuateServer(Map<String, String> params, SvcLogicContext ctx) throws APPCException {
154 Server server = null;
155 RequestContext rc = new RequestContext(ctx);
158 setTimeForMetricsLogger();
161 ctx.setAttribute(EVACUATE_STATUS, "ERROR");
163 validateParametersExist(params, ProviderAdapter.PROPERTY_INSTANCE_URL,
164 ProviderAdapter.PROPERTY_PROVIDER_NAME);
166 String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
167 String vmUrl = params.get(ProviderAdapter.PROPERTY_INSTANCE_URL);
168 VMURL vm = VMURL.parseURL(vmUrl);
170 if (validateVM(rc, appName, vmUrl, vm)) {
174 server = evacuateServerMapNestedFirst(params, server, rc, ctx, vm, vmUrl);
176 } catch (RequestFailedException e) {
177 msg = EELFResourceManager.format(Msg.EVACUATE_SERVER_FAILED, "n/a", "n/a", e.getMessage());
178 logger.error(msg, e);
179 metricsLogger.error(msg);
180 doFailure(rc, e.getStatus(), e.getMessage());
186 private Server evacuateServerMapNestedFirst(Map<String, String> params, Server server, RequestContext rqstCtx, SvcLogicContext ctx,
187 VMURL vm, String vmUrl)
188 throws APPCException {
193 IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
194 String identStr = (ident == null) ? null : ident.toString();
195 // retrieve the optional parameters
196 String rebuildVm = params.get(ProviderAdapter.PROPERTY_REBUILD_VM);
197 String targetHostId = params.get(ProviderAdapter.PROPERTY_TARGETHOST_ID);
198 String tenantName = "Unknown";//to be used also in case of exception
200 context = getContext(rqstCtx, vmUrl, identStr);
201 if (context != null) {
202 tenantName = context.getTenantName();//this variable also is used in case of exception
203 server = lookupServer(rqstCtx, context, vm.getServerId());
205 logger.debug(Msg.SERVER_FOUND, vmUrl, tenantName, server.getStatus().toString());
207 // check target host status
208 checkHostStatus(server, targetHostId, context);
210 // save hypervisor name before evacuate
211 String hypervisor = server.getHypervisor().getHostName();
213 evacuateServer(rqstCtx, server, targetHostId);
216 String hypervisorAfterEvacuate = server.getHypervisor().getHostName();
217 logger.debug("Hostname before evacuate: " + hypervisor + ", After evacuate: " + hypervisorAfterEvacuate);
219 // check hypervisor host name after evacuate. If it is unchanged, the evacuate
221 checkHypervisor(server, hypervisor, hypervisorAfterEvacuate);
223 // check VM status after evacuate
227 ctx.setAttribute(EVACUATE_STATUS, "SUCCESS");
229 // If a snapshot exists, do a rebuild to apply the latest snapshot to the evacuated server.
230 // This is the default behavior unless the optional parameter is set to FALSE.
231 if (rebuildVm == null || !"false".equalsIgnoreCase(rebuildVm)) {
232 List<Image> snapshots = server.getSnapshots();
233 if (snapshots == null || snapshots.isEmpty()) {
234 logger.debug("No snapshots available - skipping rebuild after evacuate");
235 } else if (paImpl != null) {
236 logger.debug("Executing a rebuild after evacuate");
237 paImpl.rebuildServer(params, ctx);
238 // Check error code for rebuild errors. Evacuate had set it to 200 after
239 // a successful evacuate. Rebuild updates the error code.
240 evacuateServerMapNestedSecond(server, rqstCtx, ctx, hypervisor, hypervisorAfterEvacuate);
245 } catch (ResourceNotFoundException e) {
246 msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, e, vmUrl);
248 metricsLogger.error(msg);
249 doFailure(rqstCtx, HttpStatus.NOT_FOUND_404, msg);
250 } catch (RequestFailedException e) {
251 logger.error("Request failed", e);
252 doFailure(rqstCtx, e.getStatus(), e.getMessage());
253 } catch (IOException | ZoneException e1) {
254 msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, e1, e1.getClass().getSimpleName(),
255 Operation.EVACUATE_SERVICE.toString(), vmUrl, tenantName);
256 logger.error(msg, e1);
257 metricsLogger.error(msg, e1);
258 doFailure(rqstCtx, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
263 private void evacuateServerMapNestedSecond(Server server,RequestContext rc, SvcLogicContext ctx,
264 String hypervisor,String hypervisorAfterEvacuate) {
267 String rebuildErrorCode = ctx.getAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE);
269 if (rebuildErrorCode != null) {
271 int errorCode = Integer.parseInt(rebuildErrorCode);
272 if (errorCode != HttpStatus.OK_200.getStatusCode()) {
273 logger.debug("Rebuild after evacuate failed - error code=" + errorCode
274 + ", message=" + ctx.getAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_MESSAGE));
275 msg = EELFResourceManager.format(Msg.EVACUATE_SERVER_REBUILD_FAILED,
276 server.getName(), hypervisor, hypervisorAfterEvacuate,
277 ctx.getAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_MESSAGE));
279 metricsLogger.error(msg);
280 ctx.setAttribute(EVACUATE_STATUS, "ERROR");
281 // update error message while keeping the error code the
283 doFailure(rc, HttpStatus.getHttpStatus(errorCode), msg);
285 } catch (NumberFormatException e) {
291 private void checkHostStatus(Server server, String targetHostId, Context context)
292 throws ZoneException, RequestFailedException {
293 if (isComputeNodeDown(context, targetHostId)) {
294 String msg = EELFResourceManager.format(Msg.EVACUATE_SERVER_FAILED, server.getName(), server.getId(),
295 "Target host " + targetHostId + " status is not UP/ENABLED");
297 metricsLogger.error(msg);
298 throw new RequestFailedException(EVACUATE_SERVER, msg, HttpStatus.BAD_REQUEST_400, server);
302 private void checkHypervisor(Server server, String hypervisor, String hypervisorAfterEvacuate)
303 throws RequestFailedException {
304 if (hypervisor != null && hypervisor.equals(hypervisorAfterEvacuate)) {
305 String msg = EELFResourceManager.format(Msg.EVACUATE_SERVER_FAILED, server.getName(), server.getId(),
306 "Hypervisor host " + hypervisor
307 + " after evacuate is the same as before evacuate. Provider (ex. Openstack) recovery actions may be needed.");
309 metricsLogger.error(msg);
310 throw new RequestFailedException(EVACUATE_SERVER, msg, HttpStatus.INTERNAL_SERVER_ERROR_500, server);
314 private void checkStatus(Server server) throws RequestFailedException {
315 if (server.getStatus() == Server.Status.ERROR) {
316 String msg = EELFResourceManager.format(Msg.EVACUATE_SERVER_FAILED, server.getName(), server.getId(),
317 "VM is in ERROR state after evacuate. Provider (ex. Openstack) recovery actions may be needed.");
319 metricsLogger.error(msg);
320 throw new RequestFailedException(EVACUATE_SERVER, msg, HttpStatus.INTERNAL_SERVER_ERROR_500, server);
325 * Check if a Compute node is down.
327 * This method attempts to find a given host in the list of hypervisors for a given context. The only case where a
328 * node is considered down is if a matching hypervisor is found and it's state and status are not UP/ENABLED.
330 * @param context The current context
332 * @param host The host name (short or fully qualified) of a compute node
334 * @return true if the node is determined as down, false for all other cases
336 private boolean isComputeNodeDown(Context context, String host) throws ZoneException {
337 ComputeService service = context.getComputeService();
338 boolean nodeDown = false;
340 if (host != null && ! host.isEmpty()) {
341 List<Hypervisor> hypervisors = service.getHypervisors();
342 logger.debug("List of Hypervisors retrieved: " + Arrays.toString(hypervisors.toArray()));
343 for (Hypervisor hv : hypervisors) {
344 nodeDown = isNodeDown(host, nodeDown, hv);
350 private boolean isNodeDown(String host, boolean nodeDown, Hypervisor hv) {
351 if (isHostMatchesHypervisor(host, hv)) {
352 State hstate = hv.getState();
353 Status hstatus = hv.getStatus();
354 logger.debug("Host matching hypervisor: " + hv.getHostName() + ", State/Status: " + hstate.toString()
355 + "/" + hstatus.toString());
356 if (hstate != State.UP || hstatus != Status.ENABLED) {
363 private boolean isHostMatchesHypervisor(String host, Hypervisor hypervisor) {
364 return hypervisor.getHostName().startsWith(host);
368 protected ModelObject executeProviderOperation(Map<String, String> params, SvcLogicContext context)
369 throws APPCException {
370 setMDC(Operation.EVACUATE_SERVICE.toString(), "App-C IaaS Adapter:Evacuate", ADAPTER_NAME);
371 logOperation(Msg.EVACUATING_SERVER, params, context);
373 setTimeForMetricsLogger();
375 metricsLogger.info("Executing Provider Operation: Evacuate");
376 return evacuateServer(params, context);
379 private void setTimeForMetricsLogger() {
380 long startTime = System.currentTimeMillis();
381 TimeZone tz = TimeZone.getTimeZone("UTC");
382 DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
384 long endTime = System.currentTimeMillis();
385 long duration = endTime - startTime;
386 String durationStr = String.valueOf(duration);
387 String endTimeStrUTC = df.format(new Date());
388 MDC.put("EndTimestamp", endTimeStrUTC);
389 MDC.put("ElapsedTime", durationStr);
390 MDC.put("TargetEntity", "cdp");
391 MDC.put("TargetServiceName", "evacuate server");
392 MDC.put("ClassName", "org.onap.appc.adapter.iaas.provider.operation.impl.EvacuateServer");
395 public void setProvideAdapterRef(ProviderAdapterImpl pai) {