/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.imageservercontroller.impl;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.DEFAULT_FILE;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.HTTP_DIR;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.HTTP_FAILURE_DIR;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.HTTP_FIRSTBOOT_DIR;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.HTTP_KICKSTART_DIR;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.HTTP_SUCCESS_DIR;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.PXELINUX_0_FILE;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.PXELINUX_CFG_DIR;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.SERVER_PY_FILE;
import static com.emc.storageos.imageservercontroller.ImageServerConstants.WGET_FILE;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import com.emc.storageos.computecontroller.impl.ComputeDeviceController;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.impl.EncryptionProviderImpl;
import com.emc.storageos.db.client.model.ComputeElement;
import com.emc.storageos.db.client.model.ComputeImage;
import com.emc.storageos.db.client.model.ComputeImage.ComputeImageStatus;
import com.emc.storageos.db.client.model.ComputeImageJob;
import com.emc.storageos.db.client.model.ComputeImageJob.JobStatus;
import com.emc.storageos.db.client.model.ComputeImageServer;
import com.emc.storageos.db.client.model.ComputeSystem;
import com.emc.storageos.db.client.model.EncryptionProvider;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.imageservercontroller.ComputeImageCompleter;
import com.emc.storageos.imageservercontroller.ComputeImageServerCompleter;
import com.emc.storageos.imageservercontroller.ImageServerController;
import com.emc.storageos.imageservercontroller.OsInstallCompleter;
import com.emc.storageos.imageservercontroller.exceptions.ImageServerControllerException;
import com.emc.storageos.imageservercontroller.impl.OsInstallStatusPoller.OsInstallStatus;
import com.emc.storageos.model.ResourceOperationTypeEnum;
import com.emc.storageos.networkcontroller.SSHSession;
import com.emc.storageos.security.audit.AuditLogManager;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.svcs.errorhandling.resources.InternalException;
import com.emc.storageos.volumecontroller.AsyncTask;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.workflow.Workflow;
import com.emc.storageos.workflow.WorkflowService;
import com.emc.storageos.workflow.WorkflowStepCompleter;
public class ImageServerControllerImpl implements ImageServerController {
private static final String EVENT_SERVICE_TYPE = "IMAGE_SERVER_CONTROLLER";
private static final String IMPORT_IMAGE_WF = "IMPORT_IMAGE_WORKFLOW";
private static final String IMPORT_IMAGE_TO_SERVER_STEP = "IMPORT_IMAGE_TO_SERVER_STEP";
private static final String DELETE_IMAGE_WF = "DELETE_IMAGE_WORKFLOW";
private static final String DELETE_IMAGE_STEP = "DELETE_IMAGE_STEP";
private static final String OS_INSTALL_WF = "OS_INSTALL_WORKFLOW";
private static final String OS_INSTALL_IMAGE_SERVER_CHECK_STEP = "OS_INSTALL_IMAGE_SERVER_CHECK_STEP";
private static final String OS_INSTALL_PREPARE_PXE_STEP = "OS_INSTALL_PREPARE_PXE_STEP";
private static final String OS_INSTALL_WAIT_FOR_FINISH_STEP = "OS_INSTALL_WAIT_FOR_FINISH_STEP";
private static final String ROLLBACK_NOTHING_METHOD = "rollbackNothingMethod";
private static final String TMP = "/tmp";
private static final String IMAGESERVER_VERIFY_IMPORT_IMAGE_WF = "IMAGESERVER_VERIFY_IMPORT_IMAGE_WF";
private static final String IMAGESERVER_VERIFICATION_STEP = "IMAGESERVER_VERIFICATION_STEP";
private static final String IMAGESERVER_IMPORT_IMAGES_STEP = "IMAGESERVER_IMPORT_IMAGES_STEP";
private static final Logger log = LoggerFactory.getLogger(ImageServerControllerImpl.class);
private static final String FAILED_TO_CLOSE_STR = "failed to close image server dialog";
private static final String SUCCESS = "Success";
private DbClient dbClient;
private WorkflowService workflowService;
private ComputeDeviceController computeDeviceController;
private PxeIntegrationService pxeIntegrationService;
private OsInstallStatusPoller osInstallStatusPoller;
private String imageServerErrorMsg = null;
private static final int IMAGE_SERVER_VERSION = 100;
@Autowired
private AuditLogManager _auditMgr;
private CoordinatorClient _coordinator;
private static final String IMAGEURL_PASSWORD_SPLIT_REGEX = "(.*?:){2}((?<=\\:).*(?=\\@))";
private static final String IMAGEURL_HOST_REGEX = "^*(?<=@)([^/@]++)/.*+$";
public static final String MASKED_PASSWORD = "*********";
public void setDbClient(DbClient dbClient) {
this.dbClient = dbClient;
}
public void setOsInstallStatusPoller(OsInstallStatusPoller osInstallStatusPoller) {
this.osInstallStatusPoller = osInstallStatusPoller;
}
public void setPxeIntegrationService(PxeIntegrationService pxeIntegrationService) {
this.pxeIntegrationService = pxeIntegrationService;
}
public void setWorkflowService(WorkflowService workflowService) {
this.workflowService = workflowService;
}
public void setComputeDeviceController(ComputeDeviceController computeDeviceController) {
this.computeDeviceController = computeDeviceController;
}
/**
* setter for coordinator client
* @param coordinator
*/
public void setCoordinator(CoordinatorClient coordinator) {
_coordinator = coordinator;
}
/**
* Check if the given {@link ComputeImageServer} instance has valid details
*
* @param imageServer {@link ComputeImageServer} instance
* @return true if valid else false
*/
private boolean isImageServerValid(ComputeImageServer imageServer) {
boolean valid = false;
// make sure all required fields are set
if (!StringUtils.isBlank(imageServer.getImageServerIp()) && !StringUtils.isBlank(imageServer.getImageServerUser())
&& !StringUtils.isBlank(imageServer.getTftpBootDir()) && !StringUtils.isBlank(imageServer.getImageServerSecondIp())
&& !StringUtils.isBlank(imageServer.getImageServerHttpPort())
&& !StringUtils.isBlank(imageServer.getImageServerPassword())) {
log.info("ImageServer appears valid");
valid = true;
}
return valid;
}
/**
* The following is expected to exist on the image server:
* TFTPBOOT directory
* pxelinux.0 binary
* python
* Everything else if doesn't exist, will be pushed.
*/
private boolean verifyImageServer(ComputeImageServer imageServer) {
log.info("verifyImageServer: {}", imageServer.getImageServerIp());
boolean imageServerVerified = false;
if (!isImageServerValid(imageServer)) {
imageServerErrorMsg = "Image server settings are not valid, can't verify the server";
log.warn(imageServerErrorMsg);
return imageServerVerified;
}
ImageServerDialog d = null;
try {
SSHSession session = new SSHSession();
session.connect(imageServer.getImageServerIp(), imageServer.getSshPort(),
imageServer.getImageServerUser(), imageServer.getImageServerPassword());
d = new ImageServerDialog(session, imageServer.getSshTimeoutMs());
d.init();
if (!d.directoryExists(imageServer.getTftpBootDir())) {
throw ImageServerControllerException.exceptions.imageServerNotSetup("tftpboot directory does not exist");
}
if (!d.fileExists(imageServer.getTftpBootDir() + PXELINUX_0_FILE)) {
throw ImageServerControllerException.exceptions.imageServerNotSetup("pxelinux.0 binary does not exist");
}
if (!d.fileExists("/usr/bin/python")) {
throw ImageServerControllerException.exceptions.imageServerNotSetup("python not found");
}
// check is.properties file if upgrade is needed
// perform upgrade if file is not there, or version property is not
// found or not valid or less then IMAGE_SERVER_VERSION
boolean upgradeRequired = false;
if (!d.fileExists(imageServer.getTftpBootDir() + "is.properties")) {
upgradeRequired = true;
} else {
String s = d.readFile(imageServer.getTftpBootDir() + "is.properties");
Properties p = ImageServerUtils.stringToProperties(s);
if (p.getProperty("version") == null) {
upgradeRequired = true;
} else {
try {
int version = Integer.parseInt(p.getProperty("version"));
if (version < IMAGE_SERVER_VERSION) {
upgradeRequired = true;
}
} catch (NumberFormatException e) {
upgradeRequired = true;
}
}
}
log.info("image server upgrade required: {}", upgradeRequired);
if (!d.directoryExists(imageServer.getTftpBootDir() + PXELINUX_CFG_DIR)) {
log.info("pxelinux.cfg does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir() + PXELINUX_CFG_DIR);
}
if (!StringUtils.isBlank(imageServer.getImageDir())
&& !d.directoryExists(imageServer.getTftpBootDir()
+ imageServer.getImageDir())) {
log.info("image directory does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir()
+ imageServer.getImageDir());
}
if (upgradeRequired || !d.fileExists(imageServer.getTftpBootDir() + PXELINUX_CFG_DIR + DEFAULT_FILE)) {
log.info("creating pxelinux.cfg/default");
String content = ImageServerUtils.getResourceAsString("imageserver/default");
d.writeFile(imageServer.getTftpBootDir() + PXELINUX_CFG_DIR + DEFAULT_FILE, content);
}
if (!d.directoryExists(imageServer.getTftpBootDir() + HTTP_DIR)) {
log.info("http does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir() + HTTP_DIR);
}
if (upgradeRequired || !d.fileExists(imageServer.getTftpBootDir() + HTTP_DIR + SERVER_PY_FILE)) {
log.info("creating server.py");
String content = ImageServerUtils.getResourceAsString("imageserver/server.py");
StringBuilder script = new StringBuilder(content);
ImageServerUtils.replaceAll(script, "{http.port}", imageServer.getImageServerHttpPort());
d.writeFile(imageServer.getTftpBootDir() + HTTP_DIR + SERVER_PY_FILE, script.toString());
d.chmodFile("744", imageServer.getTftpBootDir() + HTTP_DIR + SERVER_PY_FILE);
}
String pid = d.getServerPid(imageServer.getImageServerHttpPort());
if (upgradeRequired && pid != null) {
// if update required and server is running, kill it
log.info("{} is running as pid: {}, kill it", SERVER_PY_FILE, pid);
d.kill(pid);
pid = null;
}
if (pid == null) {
log.info("{} is not running, will attempt to start it", SERVER_PY_FILE);
d.cd(imageServer.getTftpBootDir() + HTTP_DIR);
d.nohup(String.format("python %s", SERVER_PY_FILE));
}
if (upgradeRequired || !d.fileExists(imageServer.getTftpBootDir() + HTTP_DIR + WGET_FILE)) {
log.info("creating wget wrapper script");
String content = ImageServerUtils.getResourceAsString("imageserver/wget");
d.writeFile(imageServer.getTftpBootDir() + HTTP_DIR + WGET_FILE, content);
}
if (!d.directoryExists(imageServer.getTftpBootDir() + HTTP_KICKSTART_DIR)) {
log.info("http/ks does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir() + HTTP_KICKSTART_DIR);
}
if (!d.directoryExists(imageServer.getTftpBootDir() + HTTP_FIRSTBOOT_DIR)) {
log.info("http/fb does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir() + HTTP_FIRSTBOOT_DIR);
}
if (!d.directoryExists(imageServer.getTftpBootDir() + HTTP_SUCCESS_DIR)) {
log.info("http/success does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir() + HTTP_SUCCESS_DIR);
}
if (!d.directoryExists(imageServer.getTftpBootDir() + HTTP_FAILURE_DIR)) {
log.info("http/failure does not exist, will create it");
d.mkdir(imageServer.getTftpBootDir() + HTTP_FAILURE_DIR);
}
// save is.properties
if (upgradeRequired) {
log.info("saving is.properties");
d.writeFile(imageServer.getTftpBootDir() + "is.properties", "version=" + IMAGE_SERVER_VERSION
+ "\nhttp_port=" + imageServer.getImageServerHttpPort());
}
log.info("image server setup was successfully verified");
imageServerVerified = true;
imageServerErrorMsg = null;
} catch (Exception e) {
log.error("Unexpected exception during image server verification: " + e.getMessage(), e);
imageServerErrorMsg = e.getMessage();
} finally {
if (d != null && d.isConnected()) {
d.close();
}
}
return imageServerVerified;
}
/**
* Import image to all available imageServer
*
* @param task {@link AsyncTask} instance
*/
@Override
public void importImageToServers(AsyncTask task) throws InternalException {
log.info("importImage");
URI ciId = task._id;
boolean wfHasSteps = false;
Workflow workflow = workflowService.getNewWorkflow(this,
IMPORT_IMAGE_WF, true, task._opId);
TaskCompleter completer = new ComputeImageCompleter(ciId, task._opId,
OperationTypeEnum.CREATE_COMPUTE_IMAGE, EVENT_SERVICE_TYPE);
try {
List<URI> ids = dbClient
.queryByType(ComputeImageServer.class, true);
for (URI imageServerId : ids) {
log.info("import to server:" + imageServerId.toString());
ComputeImageServer imageServer = dbClient.queryObject(
ComputeImageServer.class, imageServerId);
if (imageServer.getComputeImages() == null
|| !imageServer.getComputeImages().contains(
ciId.toString())) {
log.info("verify Image Server");
String verifyServerStepId = workflow.createStep(IMAGESERVER_VERIFICATION_STEP, String.format(
"Verifying ImageServer %s", imageServerId), null,
imageServerId, imageServerId.toString(), this
.getClass(),
new Workflow.Method(
"verifyComputeImageServer", imageServerId),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
workflow.createStep(IMPORT_IMAGE_TO_SERVER_STEP, String
.format("Importing image for %s", imageServerId),
verifyServerStepId, imageServerId, imageServerId.toString(), this
.getClass(),
new Workflow.Method(
"importImageMethod", ciId, imageServer, null),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
wfHasSteps = true;
}
}
if (wfHasSteps) {
workflow.executePlan(completer, SUCCESS);
}
} catch (Exception e) {
log.error("importImage caught an exception.", e);
ServiceError serviceError = DeviceControllerException.errors
.jobFailed(e);
completer.error(dbClient, serviceError);
}
}
private String sanitizeUrl(String url) {
try {
URL imageUrl = new URL(url); // NOSONAR
// ("We are using this line to verify valid URL")
String filepart = imageUrl.getFile();
String[] tempArr = StringUtils.split(filepart, "/");
StringBuilder strBuilder = new StringBuilder();
for (String string : tempArr) {
strBuilder.append("/").append(
URLEncoder.encode(string, "UTF-8"));
}
String newURL = StringUtils.replace(url, filepart,
strBuilder.toString());
new URL(newURL);
return newURL;
} catch (MalformedURLException e) {
throw ImageServerControllerException.exceptions
.urlSanitationFailure(url, e);
} catch (UnsupportedEncodingException e) {
throw ImageServerControllerException.exceptions
.urlSanitationFailure(url, e);
}
}
/**
* Extract "update02" from file path.
* Example:
* /tmp/ESX-4.1.0-update02-502767.iso
* /tmp/VMware-VMvisor-Installer-4.1.0.update02-502767.x86_64.iso
*
* @param isoPath
* @return
*/
private String extractUpdateFromFilePath(String isoPath) {
String fileName = getFileNameFromPath(isoPath);
int idx = fileName.indexOf("update");
if (idx != -1) {
String result = fileName.substring(idx).split("-")[0];
return shortenUpdateLabel(result);
} else {
return "";
}
}
/**
* Get file name from file path.
* Example: /tmp/dir1/my_file => my_file
*
* @param pathToFile
* @return
*/
private String getFileNameFromPath(String pathToFile) {
String[] tokens = pathToFile.split("/");
String fileName = tokens[tokens.length - 1]; // get last element
return fileName;
}
/**
* Translate update01 to u1, update2 to u2.
*
* @param update
* @return
*/
private String shortenUpdateLabel(String update) {
return "u" + Integer.parseInt(update.substring(6));
}
private ComputeImage getOsMetadata(ImageServerDialog d, String isoPath, String isoMountDir) throws InternalException {
ComputeImage metadata = new ComputeImage();
// is it ESXi 5x
if (d.fileExists(isoMountDir + "upgrade/metadata.xml") && d.fileExists(isoMountDir + "upgrade/profile.xml")) {
metadata = new ComputeImage();
d.cd(isoMountDir);
String cmd = String.format(ImageServerDialogProperties.getString("cmd.grepXmlValue"), "esxVersion",
"esxVersion", "upgrade/metadata.xml");
String esxVersion = d.execCommand(cmd);
cmd = String.format(ImageServerDialogProperties.getString("cmd.grepXmlValue"), "build",
"build", "upgrade/metadata.xml");
String build = d.execCommand(cmd);
metadata.setOsVersion(esxVersion);
metadata.setOsBuild(build);
metadata.setOsName("esxi");
metadata.setOsArchitecture("x86_64");
metadata.setOsUpdate(extractUpdateFromFilePath(isoPath));
metadata.setImageType(ComputeImage.ImageType.esx.name());
// figure out custom
cmd = String.format(ImageServerDialogProperties.getString("cmd.grepXmlValue"), "name",
"name", "upgrade/profile.xml");
String profileName = d.execCommand(cmd);
if (!profileName.endsWith("-standard")) {
metadata.setCustomName(profileName.replaceAll(" ", "_"));
}
d.cd(TMP);
} else {
throw ImageServerControllerException.exceptions.unknownOperatingSystem();
}
return metadata;
}
private void isSupportedImage(ComputeImage os)
throws ImageServerControllerException {
if (checkOSVersion(os) && checkOSBuildArchitecture(os)) {
log.info(String.format("metadata: %s %s %s %s %s %s",
os.getOsName(), os.getOsVersion(), os.getOsUpdate(),
os.getOsBuild(), os.getOsArchitecture(), os.getCustomName()));
} else {
throw ImageServerControllerException.exceptions
.unsupportedImageVersion(String.format(
"metadata: %s %s %s %s %s %s", os.getOsName(),
os.getOsVersion(), os.getOsUpdate(),
os.getOsBuild(), os.getOsArchitecture(),
os.getCustomName()));
}
}
/**
* check OS version
*
* @param os {@link ComputeIamge} instance
* @return
*/
private boolean checkOSVersion(ComputeImage os) {
return "esxi".equals(os.getOsName()) && os.getOsVersion() != null
&& (os.getOsVersion().startsWith("5.") || os.getOsVersion().startsWith("6."));
}
/**
* check OS build and architecture type
*
* @param os {@link ComputeIamge} instance
* @return
*/
private boolean checkOSBuildArchitecture(ComputeImage os) {
return os.getOsBuild() != null
&& os.getOsArchitecture() != null && os.getOsArchitecture().equals("x86_64");
}
private void cleanupTemp(ImageServerDialog d, String dir, String iso) {
try {
d.umount(dir);
d.rm(dir);
d.rm(iso);
} catch (Exception ignore) {
log.warn(ignore.getMessage(), ignore);
}
}
/**
* Method to import an image
*
* @param ciId {@link URI} computeImage URI
* @param imageServer {@link ComputeImageServer} imageServer instance
* @param opName operation Name
* @param stepId {@link String} step Id
*/
public void importImageMethod(URI ciId, ComputeImageServer imageServer,
String opName, String stepId) {
log.info("importImageMethod importing image {} on to imageServer {}",
ciId, imageServer.getId());
ImageServerDialog d = null;
ComputeImage ci = null;
try {
WorkflowStepCompleter.stepExecuting(stepId);
ci = dbClient.queryObject(ComputeImage.class, ciId);
SSHSession session = new SSHSession();
session.connect(imageServer.getImageServerIp(), imageServer.getSshPort(),
imageServer.getImageServerUser(), imageServer.getImageServerPassword());
d = new ImageServerDialog(session, imageServer.getSshTimeoutMs());
importImage(imageServer, ci, d);
WorkflowStepCompleter.stepSucceded(stepId);
} catch (InternalException e) {
log.error("Exception importing image: " + e.getMessage(), e);
updateFailedImages(imageServer.getId(), ci);
WorkflowStepCompleter.stepFailed(stepId, e);
} catch (Exception e) {
log.error(
"Unexpected exception importing image: " + e.getMessage(),
e);
String operationName = opName;
if (null == operationName) {
operationName = ResourceOperationTypeEnum.IMPORT_IMAGE
.getName();
}
updateFailedImages(imageServer.getId(), ci);
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions
.unexpectedException(operationName, e));
} finally {
try {
if (d != null && d.isConnected()) {
d.close();
}
} catch (Exception e) {
log.error(FAILED_TO_CLOSE_STR, e);
}
}
}
/**
* Utility method to import an image to the given computeimage server
*
* @param imageServer {@link ComputeImageServer} instance.
* @param ci {@link ComputeImage} instance
* @param imageserverDialog {@link ImageServerDialog} instance
*/
private void importImage(ComputeImageServer imageServer, ComputeImage ci,
ImageServerDialog imageserverDialog) {
log.info("Importing image {} on to {} imageServer", ci.getLabel(),
imageServer.getLabel());
String deCrpytedURL = decryptImageURLPassword(ci.getImageUrl());
deCrpytedURL = sanitizeUrl(deCrpytedURL);
String ts = String.valueOf(System.currentTimeMillis());
String[] tokens = ci.getImageUrl().split("/");
String imageName = tokens[tokens.length - 1];
String imagePath = TMP + "/" + imageName;
String tempDir = TMP + "/os" + ts + "/";
imageserverDialog.init();
log.info("connected to image server {}", imageServer.getLabel());
log.info("cd to {}", TMP);
imageserverDialog.cd(TMP);
log.info("download image");
// CTRL-12030: special characters in URL's password cause issues on
// Image Server. Adding quotes.
boolean res = imageserverDialog.wget("'" + deCrpytedURL + "'",
imageName, imageServer.getImageImportTimeoutMs());
if (res) {
log.info("downloaded image successfully on to {} imageServer",
imageServer.getLabel());
} else {
throw ImageServerControllerException.exceptions
.fileDownloadFailed(maskImageURLPassword(ci.getImageUrl()));
}
log.info("create temp dir {}", tempDir);
imageserverDialog.mkdir(tempDir);
log.info("mount image onto temp dir of {}", imageServer.getLabel());
imageserverDialog.mount(imageName, tempDir);
log.info("Analyze metadata");
ComputeImage osMetadata = getOsMetadata(imageserverDialog, imagePath,
tempDir);
isSupportedImage(osMetadata);
// make sure it is not already loaded
List<URI> ids = dbClient.queryByType(ComputeImage.class, true);
Iterator<ComputeImage> iter = dbClient.queryIterativeObjects(
ComputeImage.class, ids);
while (iter.hasNext()) {
ComputeImage existingImage = iter.next();
if (osMetadata.fullName().equals(existingImage.getImageName())
&& imageServer.getComputeImages() != null
&& imageServer.getComputeImages().contains(
existingImage.getId().toString())) {
log.error("This image is already imported, id: {}",
existingImage.getId());
cleanupTemp(imageserverDialog, tempDir, imagePath);
throw ImageServerControllerException.exceptions
.duplicateImage(osMetadata.fullName());
}
}
log.info("Compute image '" + osMetadata.fullName()
+ "' will be loaded.");
// copy OS into TFTP boot directory
String targetDir = imageServer.getTftpBootDir()
+ imageServer.getImageDir() + osMetadata.fullName();
imageserverDialog.rm(targetDir);
log.info("Saving image into target directory " + targetDir);
imageserverDialog.cpDir(tempDir, targetDir);
log.info("Saved");
log.info("Change target directory permissions to 755");
imageserverDialog.chmodDir("755", targetDir);
// save in DB
ci.setOsName(osMetadata.getOsName());
ci.setOsVersion(osMetadata.getOsVersion());
ci.setOsUpdate(osMetadata.getOsUpdate());
ci.setOsBuild(osMetadata.getOsBuild());
ci.setOsArchitecture(osMetadata.getOsArchitecture());
ci.setCustomName(osMetadata.getCustomName());
ci.setPathToDirectory(imageServer.getImageDir() + osMetadata.fullName()
+ "/");
ci.setImageName(osMetadata.fullName());
ci.setImageType(osMetadata.getImageType());
ci.setComputeImageStatus(ComputeImageStatus.AVAILABLE.toString());
dbClient.updateObject(ci);
String ciURIString = ci.getId().toString();
// update the imageServer with the successfully updated image.
if (imageServer.getComputeImages() == null) {
imageServer.setComputeImages(new StringSet());
}
imageServer.getComputeImages().add(ciURIString);
//check if this image was previously failed, if so remove from fail list
if (imageServer.getFailedComputeImages() != null &&
imageServer.getFailedComputeImages().contains(ciURIString)) {
imageServer.getFailedComputeImages().remove(ciURIString);
}
log.info("Successfully imported image {} on to {} imageServer", ci.getLabel(),
imageServer.getLabel());
dbClient.updateObject(imageServer);
// clean up
cleanupTemp(imageserverDialog, tempDir, imagePath);
}
/**
* Delete image from all available imageServers
*
* @param task {@link AsyncTask} instance
*/
@Override
public void deleteImage(AsyncTask task) throws InternalException {
log.info("deleteImage " + task._id);
URI ciId = task._id;
TaskCompleter completer = null;
try {
completer = new ComputeImageCompleter(ciId, task._opId,
OperationTypeEnum.DELETE_COMPUTE_IMAGE, EVENT_SERVICE_TYPE);
Workflow workflow = workflowService.getNewWorkflow(this,
DELETE_IMAGE_WF, true, task._opId);
List<URI> ids = dbClient
.queryByType(ComputeImageServer.class, true);
for (URI imageServerId : ids) {
ComputeImageServer imageServer = dbClient.queryObject(
ComputeImageServer.class, imageServerId);
if (imageServer.getComputeImages() != null
&& imageServer.getComputeImages().contains(
ciId.toString())) {
boolean imageServerVerified = verifyImageServer(imageServer);
if (!imageServerVerified) {
throw ImageServerControllerException.exceptions
.imageServerNotSetup("Can't delete image: "
+ imageServerErrorMsg);
}
workflow.createStep(DELETE_IMAGE_STEP, String.format(
"removing image %s", ciId), null, ciId, ciId
.toString(),
this.getClass(), new Workflow.Method(
"deleteImageMethod", ciId, imageServer.getId()),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
}
//The image being deleted/cleaned up must also be removed from the
//imageServer's failedImages list, because the image can be AVAILABLE
//but could have failed to import on some of the imageServers.
//So this cleanup needs to be performed.
if(imageServer.getFailedComputeImages() != null
&& imageServer.getFailedComputeImages().contains(
ciId.toString())) {
imageServer.getFailedComputeImages().remove(ciId.toString());
dbClient.updateObject(imageServer);
}
}
workflow.executePlan(completer, SUCCESS);
} catch (Exception e) {
log.error("deleteImage caught an exception.", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
completer.error(dbClient, serviceError);
}
}
/**
* Deletes a given image from the imageServer
* @param ciId {@link URI} compute image id
* @param imageServerId {@link URI} compute image server id
* @param stepId {@link String} step id
*/
public void deleteImageMethod(URI ciId, URI imageServerId, String stepId) {
log.info("deleteImageMethod {}", ciId);
ImageServerDialog d = null;
try {
WorkflowStepCompleter.stepExecuting(stepId);
ComputeImageServer imageServer = dbClient.queryObject(ComputeImageServer.class, imageServerId);
ComputeImage ci = dbClient.queryObject(ComputeImage.class, ciId);
SSHSession session = new SSHSession();
session.connect(imageServer.getImageServerIp(), imageServer.getSshPort(),
imageServer.getImageServerUser(), imageServer.getImageServerPassword());
d = new ImageServerDialog(session, imageServer.getSshTimeoutMs());
d.init();
log.info("connected to image server");
log.info("calling image server to delete image");
d.rm(imageServer.getTftpBootDir() + ci.getPathToDirectory());
log.info("delete done");
if (imageServer.getComputeImages() != null && imageServer.getComputeImages().contains(ciId.toString())) {
imageServer.getComputeImages().remove(ciId.toString());
dbClient.updateObject(imageServer);
}
WorkflowStepCompleter.stepSucceded(stepId);
} catch (InternalException e) {
log.error("Exception deleting image: " + e.getMessage(), e);
WorkflowStepCompleter.stepFailed(stepId, e);
} catch (Exception e) {
log.error("Unexpected exception deleting image: " + e.getMessage(), e);
String opName = ResourceOperationTypeEnum.REMOVE_IMAGE.getName();
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions.unexpectedException(opName, e));
} finally {
try {
if (d != null && d.isConnected()) {
d.close();
}
} catch (Exception e) {
log.error(FAILED_TO_CLOSE_STR, e);
}
}
}
/**
* Install OS
* @param task {@link AsyncTask}
* @param computeImageJob {@link URI} compute imageJob id
* @throws InternalException
*/
@Override
public void installOperatingSystem(AsyncTask task, URI computeImageJob)
throws InternalException {
log.info("installOperatingSystem");
Host host = dbClient.queryObject(Host.class, task._id);
ComputeElement ce = dbClient.queryObject(ComputeElement.class, host.getComputeElement());
ComputeSystem cs = dbClient.queryObject(ComputeSystem.class, ce.getComputeSystem());
ComputeImageJob job = dbClient.queryObject(ComputeImageJob.class, computeImageJob);
ComputeImageServer imageServer = dbClient.queryObject(ComputeImageServer.class, job.getComputeImageServerId());
ComputeImage img = dbClient.queryObject(ComputeImage.class, job.getComputeImageId());
TaskCompleter completer = null;
try {
completer = new OsInstallCompleter(host.getId(), task._opId, job.getId(), EVENT_SERVICE_TYPE);
boolean imageServerVerified = verifyImageServer(imageServer);
if (!imageServerVerified) {
throw ImageServerControllerException.exceptions.imageServerNotSetup("Can't install operating system: "
+ imageServerErrorMsg);
}
Workflow workflow = workflowService.getNewWorkflow(this, OS_INSTALL_WF, true, task._opId);
String waitFor = null;
waitFor = workflow.createStep(OS_INSTALL_IMAGE_SERVER_CHECK_STEP,
"image server check pre os install", waitFor,
img.getId(), img.getImageType(),
this.getClass(), new Workflow.Method("preOsInstallImageServerCheck", job.getId()),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
waitFor = workflow.createStep(OS_INSTALL_PREPARE_PXE_STEP,
"prepare pxe boot", waitFor,
img.getId(), img.getImageType(),
this.getClass(), new Workflow.Method("preparePxeBootMethod", job.getId()),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
String prepStepId = workflow.createStepId();
waitFor = computeDeviceController.addStepsPreOsInstall(workflow, waitFor, cs.getId(), host.getId(), prepStepId);
waitFor = workflow.createStep(OS_INSTALL_WAIT_FOR_FINISH_STEP,
"wait for os install to finish", waitFor,
img.getId(), img.getImageType(),
this.getClass(), new Workflow.Method("waitForFinishMethod", job.getId(), host.getHostName()),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
waitFor = computeDeviceController.addStepsPostOsInstall(workflow, waitFor, cs.getId(), ce.getId(),
host.getId(), prepStepId, job.getVolumeId());
workflow.executePlan(completer, SUCCESS);
} catch (Exception e) {
log.error("installOperatingSystem caught an exception.", e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
completer.error(dbClient, serviceError);
}
}
/**
* Performs preOs install check on the imageServer
* @param jobId {@link URI} job id
* @param stepId {@link String} step id
*/
public void preOsInstallImageServerCheck(URI jobId, String stepId) {
log.info("preOsInstallImageServerCheck {} ", jobId);
ImageServerDialog d = null;
try {
WorkflowStepCompleter.stepExecuting(stepId);
ComputeImageJob job = dbClient.queryObject(ComputeImageJob.class, jobId);
ComputeImage img = dbClient.queryObject(ComputeImage.class, job.getComputeImageId());
ComputeImageServer imageServer = dbClient.queryObject(ComputeImageServer.class, job.getComputeImageServerId());
SSHSession session = new SSHSession();
session.connect(imageServer.getImageServerIp(), imageServer.getSshPort(),
imageServer.getImageServerUser(), imageServer.getImageServerPassword());
d = new ImageServerDialog(session, imageServer.getSshTimeoutMs());
d.init();
log.info("connected to image server");
log.info("verify the image is still there");
if (!d.directoryExists(imageServer.getTftpBootDir() + img.getPathToDirectory())) {
log.error("the image is missing");
throw ImageServerControllerException.exceptions.computeImageIsMissing(img.getPathToDirectory());
}
String pid = d.getServerPid("67");
if (pid == null) {
// dhcp down
throw ImageServerControllerException.exceptions.dhcpServerNotRunning();
}
pid = d.getServerPid("69");
if (pid == null) {
// tftp down
throw ImageServerControllerException.exceptions.tftpServerNotRunning();
}
log.info("make sure the python server is running");
pid = d.getServerPid(imageServer.getImageServerHttpPort());
if (pid == null) {
log.warn("python server is not running, attempt to start it");
d.cd(imageServer.getTftpBootDir() + HTTP_DIR);
d.nohup(String.format("python %s", SERVER_PY_FILE));
pid = d.getServerPid(imageServer.getImageServerHttpPort());
if (pid == null) {
throw ImageServerControllerException.exceptions.httpPythonServerNotRunning();
}
}
WorkflowStepCompleter.stepSucceded(stepId);
} catch (InternalException e) {
log.error("Exception during image server check pre os install: " + e.getMessage(), e);
WorkflowStepCompleter.stepFailed(stepId, e);
} catch (Exception e) {
log.error("Unexpected exception during image server check pre os install: " + e.getMessage(), e);
String opName = ResourceOperationTypeEnum.INSTALL_OPERATING_SYSTEM.getName();
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions.unexpectedException(opName, e));
} finally {
try {
if (d != null && d.isConnected()) {
d.close();
}
} catch (Exception e) {
log.error(FAILED_TO_CLOSE_STR, e);
}
}
}
/**
* Prepare pxe boot method, copies the conf file
* @param jobId {@link URI} job id
* @param stepId {@link String} step id
*/
public void preparePxeBootMethod(URI jobId, String stepId) {
log.info("preparePxeBootMethod {} ", jobId);
ImageServerDialog d = null;
try {
WorkflowStepCompleter.stepExecuting(stepId);
ComputeImageJob job = dbClient.queryObject(ComputeImageJob.class, jobId);
ComputeImage img = dbClient.queryObject(ComputeImage.class, job.getComputeImageId());
ComputeImageServer imageServer = dbClient.queryObject(ComputeImageServer.class, job.getComputeImageServerId());
SSHSession session = new SSHSession();
session.connect(imageServer.getImageServerIp(), imageServer.getSshPort(),
imageServer.getImageServerUser(), imageServer.getImageServerPassword());
d = new ImageServerDialog(session, imageServer.getSshTimeoutMs());
d.init();
log.info("connected to image server");
log.info("putting pxe conf file");
pxeIntegrationService.createSession(d, job, img, imageServer);
WorkflowStepCompleter.stepSucceded(stepId);
} catch (InternalException e) {
log.error("Exception preparing pxe boot: " + e.getMessage(), e);
WorkflowStepCompleter.stepFailed(stepId, e);
} catch (Exception e) {
log.error("Unexpected exception preparing pxe boot: " + e.getMessage(), e);
String opName = ResourceOperationTypeEnum.INSTALL_OPERATING_SYSTEM.getName();
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions.unexpectedException(opName, e));
} finally {
try {
if (d != null && d.isConnected()) {
d.close();
}
} catch (Exception e) {
log.error(FAILED_TO_CLOSE_STR, e);
}
}
}
/**
* Utility wait method to check os install status
* @param jobId {@link URI} job id
* @param hostName {@link String} host name for error reporting
* @param stepId {@link String} step id
*/
public void waitForFinishMethod(URI jobId, String hostName, String stepId) {
log.info("waitForFinishMethod {}, {} ", jobId, hostName);
try {
WorkflowStepCompleter.stepExecuting(stepId);
ComputeImageJob job = dbClient.queryObject(ComputeImageJob.class, jobId);
ComputeImageServer imageServer = dbClient.queryObject(ComputeImageServer.class, job.getComputeImageServerId());
if (job.getJobStartTime() == null) {
log.info("starting the job");
job.setJobStartTime(System.currentTimeMillis());
dbClient.updateObject(job);
} else {
log.info("resuming the job");
}
OsInstallStatus status = null;
while (System.currentTimeMillis() - job.getJobStartTime() < imageServer.getOsInstallTimeoutMs()
&& status == null) {
try {
log.info("sleep for {} ms", imageServer.getJobPollingIntervalMs());
Thread.sleep(imageServer.getJobPollingIntervalMs());
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
log.info("check status for {}, after {} sec", job.getPxeBootIdentifier(),
(System.currentTimeMillis() - job.getJobStartTime()) / 1000);
status = osInstallStatusPoller.getOsInstallStatus(job.getPxeBootIdentifier());
}
if (status != null) { // it is success or failure - do clean up
ImageServerDialog d = null;
try {
SSHSession session = new SSHSession();
session.connect(imageServer.getImageServerIp(), imageServer.getSshPort(),
imageServer.getImageServerUser(), imageServer.getImageServerPassword());
d = new ImageServerDialog(session, imageServer.getSshTimeoutMs());
d.init();
d.rm(imageServer.getTftpBootDir() + HTTP_SUCCESS_DIR + job.getPxeBootIdentifier());
d.rm(imageServer.getTftpBootDir() + HTTP_FAILURE_DIR + job.getPxeBootIdentifier());
d.rm(imageServer.getTftpBootDir() + HTTP_KICKSTART_DIR + job.getPxeBootIdentifier());
d.rm(imageServer.getTftpBootDir() + HTTP_FIRSTBOOT_DIR + job.getPxeBootIdentifier());
d.rm(imageServer.getTftpBootDir() + PXELINUX_CFG_DIR + job.getPxeBootIdentifier());
d.rm(imageServer.getTftpBootDir() + PXELINUX_CFG_DIR + job.getPxeBootIdentifier() + ".boot.cfg");
} catch (Exception e) {
log.error("exception when trying to poll for status", e);
} finally {
try {
if (d != null && d.isConnected()) {
d.close();
}
} catch (Exception e) {
log.error(FAILED_TO_CLOSE_STR, e);
}
}
}
log.info("job status: {}", status);
if (status == OsInstallStatus.SUCCESS) {
log.info("session {} - marking job as SUCCESS", job.getPxeBootIdentifier());
job.setJobStatus(JobStatus.SUCCESS.name());
dbClient.updateObject(job);
WorkflowStepCompleter.stepSucceded(stepId);
} else if (status == OsInstallStatus.FAILURE) {
log.info("session {} - marking job as FAILED", job.getPxeBootIdentifier());
job.setJobStatus(JobStatus.FAILED.name());
dbClient.updateObject(job);
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions.osInstallationFailed(hostName, "failure in the post-install"));
} else { // timed out
log.info("session {} - marking job as TIMEDOUT", job.getPxeBootIdentifier());
job.setJobStatus(JobStatus.TIMEDOUT.name());
dbClient.updateObject(job);
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions.osInstallationTimedOut(hostName, imageServer.getOsInstallTimeoutMs() / 1000));
}
} catch (InternalException e) {
log.error("Exception waiting for finish: " + e.getMessage(), e);
WorkflowStepCompleter.stepFailed(stepId, e);
} catch (Exception e) {
log.error("Unexpected exception waiting for finish: " + e.getMessage(), e);
String opName = ResourceOperationTypeEnum.INSTALL_OPERATING_SYSTEM.getName();
WorkflowStepCompleter.stepFailed(stepId,
ImageServerControllerException.exceptions.unexpectedException(opName, e));
}
}
/**
* This is needed if any of the workflow steps have a real rollback method.
*
* @param stepId
*/
public void rollbackNothingMethod(String stepId) {
WorkflowStepCompleter.stepSucceded(stepId);
}
/**
* Method to verify and import images on the imageserver
*
* @param task {@link AsyncTask} instance
* @param opName operation Name
*
*/
@Override
public void verifyImageServerAndImportExistingImages(AsyncTask task, String opName) {
TaskCompleter completer = null;
log.info("Verifying imageServer and importing any existing images on to the server");
try {
URI computeImageServerID = task._id;
completer = new ComputeImageServerCompleter(computeImageServerID,
task._opId,
OperationTypeEnum.IMAGESERVER_VERIFY_IMPORT_IMAGES,
EVENT_SERVICE_TYPE);
Workflow workflow = workflowService.getNewWorkflow(this,
IMAGESERVER_VERIFY_IMPORT_IMAGE_WF, true, task._opId);
workflow.createStep(IMAGESERVER_VERIFICATION_STEP, String.format(
"Verfiying ImageServer %s", computeImageServerID), null,
computeImageServerID, computeImageServerID.toString(), this
.getClass(),
new Workflow.Method(
"verifyComputeImageServer", computeImageServerID),
new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
List<ComputeImage> computeImageList = getAllComputeImages();
if (!CollectionUtils.isEmpty(computeImageList)) {
ComputeImageServer imageServer = dbClient.queryObject(
ComputeImageServer.class, computeImageServerID);
for (ComputeImage computeImage : computeImageList) {
if (null == imageServer.getComputeImages()
|| !imageServer.getComputeImages().contains(
computeImage.getId().toString())) {
StringBuilder msg = new StringBuilder(
"Importing image ");
msg.append(computeImage.getLabel()).append(
" on to imageServer - ");
msg.append(imageServer.getImageServerIp()).append(".");
workflow.createStep(IMAGESERVER_IMPORT_IMAGES_STEP, msg
.toString(), IMAGESERVER_VERIFICATION_STEP,
computeImageServerID, computeImageServerID
.toString(), this.getClass(),
new Workflow.Method("importImageMethod",
computeImage.getId(), imageServer,
opName), new Workflow.Method(ROLLBACK_NOTHING_METHOD), null);
}
}
}
workflow.executePlan(completer, SUCCESS);
} catch (Exception ex) {
log.error(
"Unexpected exception waiting for finish: "
+ ex.getMessage(),
ex);
}
}
/**
* Method to fetch all compute images present in the db.
*
* @return {@link List<ComputeImage>}
*/
private List<ComputeImage> getAllComputeImages() {
List<ComputeImage> imageList = null;
List<URI> imageURIList = dbClient.queryByType(ComputeImage.class, true);
if (imageURIList == null || !imageURIList.iterator().hasNext()) {
log.info("There are no images to be imported.");
} else {
imageList = dbClient.queryObject(ComputeImage.class, imageURIList);
if (CollectionUtils.isEmpty(imageList)) {
log.error("Could not find the ComputeImage's for the Ids {}",
imageURIList.toString());
}
}
return imageList;
}
/**
* This method verifies if the given image Server is a valid imageServer.
*
* @param imageServerId {@link URI} of ComputeImageServer
* @param stepId workflow stepid being executed.
*/
public void verifyComputeImageServer(URI imageServerId, String stepId) {
log.info("entering method verifyComputeImageServer");
WorkflowStepCompleter.stepExecuting(stepId);
ComputeImageServer imageServer = dbClient.queryObject(
ComputeImageServer.class, imageServerId);
if (verifyImageServer(imageServer)) {
imageServer
.setComputeImageServerStatus(ComputeImageServer.ComputeImageServerStatus.AVAILABLE
.name());
dbClient.updateObject(imageServer);
WorkflowStepCompleter.stepSucceded(stepId);
} else {
log.error("Unable to verify imageserver");
imageServer
.setComputeImageServerStatus(ComputeImageServer.ComputeImageServerStatus.NOT_AVAILABLE
.name());
dbClient.updateObject(imageServer);
WorkflowStepCompleter
.stepFailed(
stepId,
ImageServerControllerException.exceptions
.unexpectedException(
OperationTypeEnum.IMAGESERVER_VERIFY_IMPORT_IMAGES
.name(),
new Exception(
"Unable to verify imageserver")));
}
}
/**
* Updates the imageServer with the image that failed import, this method updates
* it as failed only after making sure that the image was not previously successful.
* @param imageServerURI {@link URI} imageServerURI instance to which import was made.
* @param image {@link ComputeImage} instance that failed the import.
*/
private void updateFailedImages(URI imageServerURI, ComputeImage image) {
if (null != imageServerURI && null != image) {
String imageURIStr = image.getId().toString();
log.info("updateFailedImages : update failed image import details.");
// first fetch updated imageServer details from DB and
// verify if image was previously loaded successfully on to
// the imageServer, if so then skip updating it as failed else
// update it as failed.
ComputeImageServer imageServer = dbClient.queryObject(
ComputeImageServer.class, imageServerURI);
if (imageServer.getComputeImages() == null
|| !imageServer.getComputeImages().contains(imageURIStr)) {
// update the imageServer with the failed image.
if (imageServer.getFailedComputeImages() == null) {
imageServer.setFailedComputeImages(new StringSet());
}
log.info(
"Image - {} failed to import on imageServer - {}",
image.getLabel(), imageServer.getLabel());
imageServer.getFailedComputeImages().add(imageURIStr);
dbClient.updateObject(imageServer);
}
}
}
/**
* Method to decrypt the imageURL password before it can be used.
* This method also takes care of encoding the password before use.
* @param imageUrl {@link String} compute image URL string
* @return {@link String}
*/
private String decryptImageURLPassword(String imageUrl) {
String password = extractPasswordFromImageUrl(imageUrl);
if (StringUtils.isNotBlank(password)) {
String encPwd = null;
try {
EncryptionProviderImpl encryptionProviderImpl = new EncryptionProviderImpl();
encryptionProviderImpl.setCoordinator(_coordinator);
encryptionProviderImpl.start();
EncryptionProvider encryptionProvider = encryptionProviderImpl;
encPwd = URLEncoder.encode(encryptionProvider.decrypt(Base64
.decodeBase64(password)), "UTF-8");
return StringUtils.replace(imageUrl, ":" + password + "@", ":"
+ encPwd + "@");
} catch (UnsupportedEncodingException e) {
log.warn(
"Unable to encode compute image password '{}'."
+ "Special characters may cause issues loading compute image.",
imageUrl, e.getMessage());
} catch (Exception e) {
log.error("Cannot decrypt compute image password :"
+ e.getLocalizedMessage());
e.printStackTrace();
throw e;
}
}
return imageUrl;
}
/**
* Extract password if present from the given imageUrl string
* @param imageUrl {@link String} image url
* @return {@link String} password
*/
public static String extractPasswordFromImageUrl(String imageUrl)
{
Pattern r = Pattern.compile(IMAGEURL_PASSWORD_SPLIT_REGEX);
Matcher m = r.matcher(imageUrl);
String password = null;
if (m.find() && m.groupCount() >= 2
&& StringUtils.isNotBlank(m.group(2))) {
password = m.group(2);
Pattern hostpattern = Pattern.compile(IMAGEURL_HOST_REGEX);
Matcher hostMatcher = hostpattern.matcher(password);
if(hostMatcher.find()) {
String preHostregex = "^(.*?)\\@"+hostMatcher.group(1);
Pattern pwdPattern = Pattern.compile(preHostregex);
Matcher pwdMatcher = pwdPattern.matcher(password);
if(pwdMatcher.find())
{
password = pwdMatcher.group(1);
}
}
}
return password;
}
/**
* Mask the encrypted password for UI
* @param imageUrl {@link String} image url
* @return {@link String} password masked image url
*/
public static String maskImageURLPassword(String imageUrl) {
String password = extractPasswordFromImageUrl(imageUrl);
String maskedPasswordURL = imageUrl;
if (StringUtils.isNotBlank(password)) {
maskedPasswordURL = StringUtils.replace(imageUrl, ":" + password + "@", ":"
+ MASKED_PASSWORD + "@");
}
return maskedPasswordURL;
}
}