/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.systemservices.impl.resource;
import static com.emc.storageos.coordinator.client.model.Constants.CONTROL_NODE_ID_PATTERN;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.emc.storageos.coordinator.client.model.Constants;
import com.emc.storageos.coordinator.client.model.PowerOffState;
import com.emc.storageos.coordinator.client.service.DrUtil;
import com.emc.storageos.security.audit.AuditLogManager;
import com.emc.storageos.security.authentication.StorageOSUser;
import com.emc.storageos.security.authorization.CheckPermission;
import com.emc.storageos.security.authorization.Role;
import com.emc.storageos.security.dbInfo.DbInfoUtils;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.services.ServicesMetadata;
import com.emc.storageos.services.util.AlertsLogger;
import com.emc.storageos.services.util.Exec;
import com.emc.storageos.services.util.FileUtils;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.systemservices.exceptions.LocalRepositoryException;
import com.emc.storageos.systemservices.exceptions.SysClientException;
import com.emc.storageos.systemservices.impl.client.SysClientFactory;
import com.emc.storageos.systemservices.impl.ipreconfig.IpReconfigManager;
import com.emc.storageos.systemservices.impl.property.PropertyManager;
import com.emc.storageos.systemservices.impl.recovery.RecoveryManager;
import com.emc.storageos.systemservices.impl.upgrade.CoordinatorClientExt;
import com.emc.storageos.systemservices.impl.upgrade.LocalRepository;
import com.emc.storageos.systemservices.impl.util.DbRepairStatusHandler;
import com.emc.storageos.systemservices.impl.vdc.VdcManager;
import com.emc.vipr.model.sys.ClusterInfo;
import com.emc.vipr.model.sys.ipreconfig.ClusterIpInfo;
import com.emc.vipr.model.sys.ipreconfig.ClusterNetworkReconfigStatus;
import com.emc.vipr.model.sys.recovery.DbOfflineStatus;
import com.emc.vipr.model.sys.recovery.DbRepairStatus;
import com.emc.vipr.model.sys.recovery.RecoveryPrecheckStatus;
import com.emc.vipr.model.sys.recovery.RecoveryStatus;
import com.sun.jersey.api.client.ClientHandlerException;
/**
* Control service is used to
* restart a service on a specified node
* reboot a node
*/
@Path("/control/")
public class ControlService {
@Autowired
private AuditLogManager _auditMgr;
private static final Logger _log = LoggerFactory.getLogger(ConfigService.class);
private static final AlertsLogger _alertsLog = AlertsLogger.getAlertsLogger();
private static final String EVENT_SERVICE_TYPE = "control";
private CoordinatorClientExt _coordinator = null;
@Autowired
private RecoveryManager recoveryManager;
@Autowired
private DbRepairStatusHandler dbRepairStatusHandler;
@Autowired
private IpReconfigManager ipreconfigManager;
@Autowired
private PropertyManager propertyManager;
private final static String FORCE = "1";
@Autowired
private VdcManager vdcManager;
@Context
protected SecurityContext sc;
private ArrayList<String> aliveNodes = new ArrayList<String>();
/**
* Get StorageOSUser from the security context
*
* @return
*/
protected StorageOSUser getUserFromContext() {
if (!hasValidUserInContext()) {
throw APIException.forbidden.invalidSecurityContext();
}
return (StorageOSUser) sc.getUserPrincipal();
}
/**
* Determine if the security context has a valid StorageOSUser object
*
* @return true if the StorageOSUser is present
*/
protected boolean hasValidUserInContext() {
if ((sc != null) && (sc.getUserPrincipal() instanceof StorageOSUser)) {
return true;
} else {
return false;
}
}
private enum ControlState {
RUN,
RESTART,
REBOOT,
END
}
private volatile ControlState controlState = ControlState.RUN;
private final String lock = "Lock";
public void setProxy(CoordinatorClientExt proxy) {
_coordinator = proxy;
}
@PostConstruct
public void initControlThread() {
new Thread(new Runnable() {
@Override
public void run() {
_log.info("Control thread started.");
synchronized (lock) {
while (!controlState.equals(ControlState.END)) {
if (controlState.equals(ControlState.REBOOT)) {
_log.info("Node is being rebooted.");
try {
controlState = ControlState.END;
LocalRepository.getInstance().reboot();
} catch (LocalRepositoryException e1) {
_log.error("Error rebooting: {}", e1.toString());
controlState = ControlState.RUN;
}
} else if (controlState.equals(ControlState.RESTART)) {
_log.info("Restarting syssvc.");
try {
controlState = ControlState.END;
System.exit(0);
} catch (LocalRepositoryException e2) {
_log.error("Error restarting syssvc: {}", e2.toString());
controlState = ControlState.RUN;
}
} else {
try {
lock.wait();
} catch (InterruptedException e) {
_log.warn("Control thread interrupted", e);
}
}
}
}
}
}).start();
}
@PreDestroy
public void destroyControlThread() {
synchronized (lock) {
controlState = ControlState.END;
lock.notifyAll();
}
}
/**
* Internal call to reset db data for vapp node recovery include cleanup db file,stop db service,create startup file.
*/
@POST
@Path("internal/node/db-reset")
@Produces({ MediaType.APPLICATION_JSON })
public Response resetDbdata(@QueryParam("node_id") String nodeId ) {
_log.info("stop dbsvc/geosvc for node recovery");
LocalRepository localRepository = LocalRepository.getInstance();
localRepository.stop("geodbsvc");
localRepository.stop("dbsvc");
purgeDbData();
return Response.ok().build();
}
/**
* clean up database file and create hibernate file
*/
private void purgeDbData () {
final String startupModeFile = "startupmode";
final String startupModeContent = startupModeFile +"=hibernate";
final String dataDir = "/data/";
final String dbDir = dataDir + "db/";
final String geodbDir=dataDir +"geodb/";
final String dbFile= dbDir + startupModeFile;
final String geodbFile=geodbDir + startupModeFile;
ArrayList<String> cleanDb = new ArrayList<>();
cleanDb.add("/usr/bin/rm");
cleanDb.add("-fr");
File dbDirFile = new File(dbDir);
File [] files = dbDirFile.listFiles();
for (File file: files){
cleanDb.add(file.toString());
}
dbDirFile = new File(geodbDir);
files = dbDirFile.listFiles();
for (File file: files){
cleanDb.add(file.toString());
}
_log.info("clean db for node recovery {}",cleanDb);
String[] cleanDbarray = new String [cleanDb.size()];
cleanDbarray = cleanDb.toArray(cleanDbarray);
Exec.Result result = Exec.exec(Exec.DEFAULT_CMD_TIMEOUT, cleanDbarray);
_log.info("result code is {}",result.getExitValue());
try {
FileUtils.writePlainFile(dbFile,startupModeContent.getBytes());
FileUtils.writePlainFile(geodbFile,startupModeContent.getBytes());
}catch (IOException e ) {
_log.error("Error when create db startup mode file {}",e);
}
}
/**
* Restart a service on a virtual machine
*
* @brief Restart a service on a virtual machine
* @param nodeId Virtual machine id (e.g. vipr1)
* @param nodeName node name of Virtual machine
* @prereq none
* @param name Service name
*/
@POST
@Path("service/restart")
@CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response restartService(@QueryParam("node_id") String nodeId, @QueryParam("name") String name, @QueryParam("node_name") String nodeName) {
nodeId=determineNodeId(nodeId,nodeName);
List<String> controlNodeServiceNames = ServicesMetadata.getControlNodeServiceNames();
if (_coordinator.getMyNodeId().equals(nodeId)) {
if (!controlNodeServiceNames.contains(name)) {
throw APIException.badRequests.parameterIsNotOneOfAllowedValues("service name", controlNodeServiceNames.toString());
}
auditControl(OperationTypeEnum.RESTART_SERVICE,
AuditLogManager.AUDITLOG_SUCCESS, null, name, nodeId);
return restartNodeService(name);
} else {
// get endpoint of node
URI endpoint = _coordinator.getNodeEndpoint(nodeId);
if (endpoint == null) {
throw APIException.badRequests.parameterIsNotValid("node id");
}
// check available service name exists
boolean isControlNode = CONTROL_NODE_ID_PATTERN.matcher(nodeId).matches();
List<String> availableServices = isControlNode ?
controlNodeServiceNames : ServicesMetadata.getExtraNodeServiceNames();
if (!availableServices.contains(name)) {
throw APIException.badRequests.parameterIsNotOneOfAllowedValues("service name", availableServices.toString());
}
try {
SysClientFactory.getSysClient(endpoint)
.post(URI.create(SysClientFactory.URI_RESTART_SERVICE.getPath() + "?name=" + name), null, null);
} catch (SysClientException e) {
throw APIException.internalServerErrors.serviceRestartError(name, nodeId);
}
auditControl(OperationTypeEnum.RESTART_SERVICE,
AuditLogManager.AUDITLOG_SUCCESS, null, name, nodeId);
return Response.status(Response.Status.ACCEPTED).build();
}
}
@POST
@Path("internal/service/restart")
@Produces({ MediaType.APPLICATION_JSON })
public Response restartNodeService(@QueryParam("name") String name) {
_log.info("Restart node service: {}", name);
// restart service
if (name.equals("syssvc")) {
synchronized (lock) {
_log.info("Notify the control thread!");
controlState = ControlState.RESTART;
lock.notifyAll();
}
_log.info("Activated the control thread!");
return Response.status(Response.Status.ACCEPTED).build(); // Return the accepted status code
} else {
try {
LocalRepository.getInstance().restart(name);
} catch (LocalRepositoryException e) {
throw APIException.internalServerErrors.serviceRestartError(name, _coordinator.getMyNodeId());
}
return Response.ok().build();
}
}
/**
* Reboot a virtual machine
*
* @brief Reboot a virtual machine
* @param nodeId Virtual machine id (e.g. vipr1)
* @param nodeName node name of Virtual machine
* @prereq none
*/
@POST
@Path("node/reboot")
@CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response rebootNode(@QueryParam("node_id") String nodeId, @QueryParam("node_name") String nodeName) {
nodeId=determineNodeId(nodeId,nodeName);
_log.info("Reboot node: "+nodeId);
if(_coordinator.getMyNodeId().equals(nodeId)){
auditControl(OperationTypeEnum.REBOOT_NODE, AuditLogManager.AUDITLOG_SUCCESS,
null, nodeId);
return rebootNode();
} else {
// Otherwise get endpoint of node
URI endpoint = _coordinator.getNodeEndpoint(nodeId);
if (endpoint == null) {
throw APIException.badRequests.parameterIsNotValid("node id");
}
try {
SysClientFactory.getSysClient(endpoint)
.post(SysClientFactory.URI_REBOOT_NODE, null, null);
} catch (SysClientException e) {
throw APIException.internalServerErrors.sysClientError("reboot node");
}
auditControl(OperationTypeEnum.REBOOT_NODE, AuditLogManager.AUDITLOG_SUCCESS,
null, nodeId);
return Response.status(Response.Status.ACCEPTED).build();
}
}
@POST
@Path("internal/node/reboot")
@Produces({ MediaType.APPLICATION_JSON })
public Response rebootNode() {
_log.info("Reboot node");
synchronized (lock) {
_log.info("Notify the control thread!");
controlState = ControlState.REBOOT;
lock.notifyAll();
}
_log.info("Activated the control thread!");
return Response.status(Response.Status.ACCEPTED).build(); // Return the accepted status code
}
@POST
@Path("internal/node/poweroff-agreement")
@Produces({ MediaType.APPLICATION_JSON })
public Response receivePoweroffAgreement(@QueryParam("sender") String svcId) {
_log.info("Receiving poweroff agreement");
vdcManager.getPoweroffAgreementsKeeper().add(svcId);
return Response.ok().build();
}
/**
* This method will set the target poweroff state to PowerOffState.State.START
* If UpgradeManager sees the target poweroff state is not NONE,
* node will start to sync with each other's poweroff state until they agree to power off.
* Node's poweroff state will change from NOTICED to ACKNOWLEDGED. During the process, all nodes
* are checked to see each other's state until they can move to next state.
* After all nodes move to ACKNOWLEDGED state, they move to POWEROFF state,
* in which, they are free to power off
*
* @brief Power off ViPR controller appliance
* @prereq none
* @return Status of ViPR controller appliance
* @throws Exception
*/
@POST
@Path("cluster/poweroff")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response powerOffCluster(@QueryParam("force") String forceSet) throws Exception {
_log.debug("Poweroff cluster");
PowerOffState.State targetPoweroffState = _coordinator.getTargetInfo(PowerOffState.class).getPowerOffState();
_log.info("Current target poweroff state is: {}", targetPoweroffState.toString());
if (targetPoweroffState.equals(PowerOffState.State.FORCESTART) || targetPoweroffState.equals(PowerOffState.State.START)) {
return Response.status(Response.Status.CONFLICT)
.entity("A poweroff proccess is in progress, cannot accept another poweroff request.").build();
}
PowerOffState poweroffState;
if (FORCE.equals(forceSet)) {
poweroffState = new PowerOffState(PowerOffState.State.FORCESTART);
} else {
poweroffState = new PowerOffState(PowerOffState.State.START);
}
// set target poweroff state to START or FORCESTART
try {
_coordinator.setTargetInfo(poweroffState, false);
vdcManager.wakeupOtherNodes();
_alertsLog.warn("power off start");
} catch (ClientHandlerException e) {
if (!FORCE.equals(forceSet)) {
throw APIException.internalServerErrors.poweroffWakeupError(e);
} else {
// if the force option is specified, ignore sysclient exceptions since the poweroff will succeed anyways.
_log.warn("failed to wakeup all nodes. Will poweroff the cluster by force.");
}
} catch (Exception e) {
throw APIException.internalServerErrors.setObjectToError("target poweroff state", "coordinator", e);
}
// ignore audit log for internal calls
if (sc != null && sc.getUserPrincipal() != null) {
auditControl(OperationTypeEnum.POWER_OFF_CLUSTER, AuditLogManager.AUDITLOG_SUCCESS, null);
}
try {
return Response.status(Response.Status.ACCEPTED).build();
} finally {
vdcManager.wakeup();
}
}
/**
* Power off current node
*/
@POST
@Path("internal/node/poweroff")
@Produces({ MediaType.APPLICATION_JSON })
public Response powerOffNode() {
_log.info("Poweroff node");
recoveryManager.poweroff();
return Response.status(Response.Status.ACCEPTED).build(); // Return the accepted status code
}
/**
* Internal call to power off current cluster for DR. Use trusted HMAC key to authenticate.
*/
@POST
@Path("internal/cluster/poweroff")
@Produces({ MediaType.APPLICATION_JSON })
public Response internalPowerOffCluster(@QueryParam("force") String forceSet) throws Exception {
_log.info("Poweroff cluster");
return powerOffCluster(forceSet);
}
/**
* Internal call to get cluster info for DR. Remote site could use this call the check cluster state
*/
@GET
@Path("internal/cluster/info")
@Produces({ MediaType.APPLICATION_JSON })
public ClusterInfo internalGetClusterInfo() {
ClusterInfo clusterInfo = _coordinator.getClusterInfo();
_log.info("Get cluster info {}", clusterInfo);
return clusterInfo;
}
/**
* Trigger minority node recovery
*/
@POST
@Path("cluster/recovery")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response clusterRecovery() throws Exception {
_log.info("Received a cluster recovery request");
try {
if (ipreconfigManager.underIpReconfiguration()) {
String errstr = "Failed to trigger node recovery, ip reconfiguration is ongoing...";
_log.warn(errstr);
throw new IllegalStateException(errstr);
}
recoveryManager.triggerNodeRecovery();
auditControl(OperationTypeEnum.RECOVER_NODES, AuditLogManager.AUDITLOG_SUCCESS, null);
} catch (Exception e) {
auditControl(OperationTypeEnum.RECOVER_NODES, AuditLogManager.AUDITLOG_FAILURE, null);
throw APIException.internalServerErrors.triggerRecoveryFailed(e.getMessage());
}
_log.info("Accepted the cluster recovery request");
return Response.status(Response.Status.ACCEPTED).build();
}
/**
* Show node recovery status
*
* @brief Show node recovery status
* @prereq none
* @return Node recovery status information
* @throws Exception
*/
@GET
@Path("cluster/recovery")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.SYSTEM_MONITOR })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public RecoveryStatus getRecoveryStatus() throws Exception {
_log.info("Received a getting cluster recovery status request");
return recoveryManager.queryNodeRecoveryStatus();
}
/**
* Get the db repair status.
*
* @brief Show db repair status
* @prereq none
* @return db repair status
* @throws Exception
*/
@GET
@Path("cluster/dbrepair-status")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.SYSTEM_MONITOR })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public DbRepairStatus getDbRepairStatus() throws Exception {
_log.info("Received a getting db repair status request");
if (_coordinator.isMyDbSvcGood(Constants.DBSVC_NAME) && _coordinator.isMyDbSvcGood(Constants.GEODBSVC_NAME)) {
return getDbRepairStatusFromLocalNode();
} else {
URI endpoint = _coordinator.getNodeEndpointWithGoodDbsvc();
_log.info("Dbsvc/Geodbsvc on current node is not started yet, swith to another syssvc endpoint " + endpoint);
if (endpoint == null) {
throw APIException.internalServerErrors.sysClientError("No valid syssvc endpoint for db repair status check");
}
try {
return SysClientFactory.getSysClient(endpoint)
.get(SysClientFactory.URI_GET_DBREPAIR_STATUS, DbRepairStatus.class, null);
} catch (SysClientException e) {
throw APIException.internalServerErrors.sysClientError("db repair status");
}
}
}
/**
* Retrieve db repair status from dbsvc/geodbsvc on current node
*/
@GET
@Path("internal/node/dbrepair-status")
@Produces({ MediaType.APPLICATION_JSON })
public DbRepairStatus getDbRepairStatusFromLocalNode() throws Exception {
_log.info("Check db repair status");
return dbRepairStatusHandler.getDbRepairStatus();
}
@GET
@Path("cluster/recovery/precheck-status")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public RecoveryPrecheckStatus recoveryPrecheck () {
RecoveryPrecheckStatus recoveryPrecheckStatus = new RecoveryPrecheckStatus();
DrUtil drUtil = new DrUtil(_coordinator.getCoordinatorClient());
if (drUtil.isMultivdc() || drUtil.isMultisite()) {
recoveryPrecheckStatus.setStatus(RecoveryPrecheckStatus.Status.VAPP_IN_DR_OR_GEO);
return recoveryPrecheckStatus;
}
ArrayList<String> nodeList = _coordinator.getAllNodeIds();
HashSet<String> unvaliableNodeSet = new HashSet<>();
ArrayList<String> recoverableNodeList = new ArrayList<>();
for (String nodeId : nodeList) {
Long dbOfflineTime = DbInfoUtils.getDbOfflineTime(_coordinator.getCoordinatorClient(), Constants.DBSVC_NAME, nodeId);
if (dbOfflineTime != null ) {
unvaliableNodeSet.add(nodeId);
} else {
dbOfflineTime = DbInfoUtils.getDbOfflineTime(_coordinator.getCoordinatorClient(), Constants.GEODBSVC_NAME, nodeId);
if (dbOfflineTime != null) {
unvaliableNodeSet.add(nodeId);
}
}
try {
DbOfflineStatus dbOfflineStatus = SysClientFactory.getSysClient(_coordinator.getNodeEndpoint(nodeId)).get(SysClientFactory.URI_GET_DB_OFFLINE_STATUS,
DbOfflineStatus.class, null);
if (dbOfflineStatus.getOutageTimeExceeded()) {
recoverableNodeList.add(nodeId);
unvaliableNodeSet.add(nodeId);
}
} catch (Exception e) {
recoveryPrecheckStatus.setStatus(RecoveryPrecheckStatus.Status.NODE_UNREACHABLE);
_log.warn("Failed to check dbOfflineStatus on {} :{}",nodeId,e.getMessage());
}
}
ArrayList<String> unvaliableNodeList = new ArrayList<>(unvaliableNodeSet);
setRecoverPrecheckStatus(recoveryPrecheckStatus, unvaliableNodeList, recoverableNodeList, nodeList.size());
recoveryPrecheckStatus.setUnavailables(unvaliableNodeList);
recoveryPrecheckStatus.setRecoverables(recoverableNodeList);
return recoveryPrecheckStatus;
}
private void setRecoverPrecheckStatus(RecoveryPrecheckStatus recoveryPrecheckStatus ,ArrayList<String> unvaliableNodeList, ArrayList<String> recoverableNodeList ,int size) {
if (recoveryPrecheckStatus.getStatus() == null || !recoveryPrecheckStatus.getStatus().equals(RecoveryPrecheckStatus.Status.NODE_UNREACHABLE)) {
if (!unvaliableNodeList.isEmpty()) {
Collections.sort(unvaliableNodeList);
Collections.sort(recoverableNodeList);
if (!unvaliableNodeList.equals(recoverableNodeList)) {
recoveryPrecheckStatus.setStatus(RecoveryPrecheckStatus.Status.CORRUPTED_NODE_FOR_OTHER_REASON);
}else {
if (recoverableNodeList.size() < (size / 2 + 1)) { /*corrupted nodes is less than quorum nodes*/
recoveryPrecheckStatus.setStatus(RecoveryPrecheckStatus.Status.RECOVERY_NEEDED);
}else {
recoveryPrecheckStatus.setStatus(RecoveryPrecheckStatus.Status.CORRUPTED_NODE_COUNT_MORE_THAN_QUORUM);
}
}
} else {
recoveryPrecheckStatus.setStatus(RecoveryPrecheckStatus.Status.ALL_GOOD);
}
}
}
@GET
@Path("internal/node/dbsvc-offline-status")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public DbOfflineStatus checkDbOfflineTime() {
_log.info("Check db offline time");
try {
DbInfoUtils.checkDBOfflineInfo(_coordinator.getCoordinatorClient(), Constants.DBSVC_NAME , "/data/db", false);
DbInfoUtils.checkDBOfflineInfo(_coordinator.getCoordinatorClient(), Constants.GEODBSVC_NAME , "/data/geodb", false);
}catch (IllegalStateException e){
return new DbOfflineStatus(true);
}
return new DbOfflineStatus(false);
}
/**
* Trigger ip reconfiguration
*/
@POST
@Path("cluster/ipreconfig")
@CheckPermission(roles = { Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response clusterIpReconfig(ClusterIpInfo clusterIpInfo,
@DefaultValue("reboot") @QueryParam("postOperation") String postOperation) throws Exception {
_log.info("Received a cluster ip reconfiguration request");
try {
ipreconfigManager.triggerIpReconfig(clusterIpInfo, postOperation);
auditControl(OperationTypeEnum.RECONFIG_IP, AuditLogManager.AUDITLOG_SUCCESS, null);
} catch (Exception e) {
auditControl(OperationTypeEnum.RECONFIG_IP, AuditLogManager.AUDITLOG_FAILURE, null);
throw APIException.internalServerErrors.triggerIpReconfigFailed(e.getMessage());
}
_log.info("Accepted the cluster ip reconfiguration request");
return Response.status(Response.Status.ACCEPTED).build();
}
/**
* Query ip reconfiguration status
*/
@GET
@Path("cluster/ipreconfig_status")
@CheckPermission(roles = { Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public ClusterNetworkReconfigStatus getClusterNetworkReconfigStatus() throws Exception {
_log.info("Querying cluster ip reconfiguration status");
return ipreconfigManager.queryClusterNetworkReconfigStatus();
}
/**
* Query current ip configuration
*/
@GET
@Path("cluster/ipinfo")
@CheckPermission(roles = { Role.SECURITY_ADMIN, Role.RESTRICTED_SECURITY_ADMIN })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public ClusterIpInfo getClusterIpinfo() throws Exception {
_log.info("Querying cluster ip configuration");
return ipreconfigManager.queryCurrentClusterIpinfo();
}
/**
* Record audit log for control service
*
* @param auditType Type of AuditLog
* @param operationalStatus Status of operation
* @param description Description for the AuditLog
* @param descparams Description paramters
*/
public void auditControl(OperationTypeEnum auditType,
String operationalStatus,
String description,
Object... descparams) {
URI username = null;
username = URI.create(getUserFromContext().getName());
_auditMgr.recordAuditLog(null, username,
EVENT_SERVICE_TYPE,
auditType,
System.currentTimeMillis(), operationalStatus,
description,
descparams);
}
/**
* Verify only one parameter is provided for selecting node
* Determine if nodeId should be used or nodeName should be converted to nodeId and used
*
* @param nodeId Id of the node
* @param nodeName Name of the node
*/
private String determineNodeId(String nodeId, String nodeName){
if (nodeName != null) {
//check that nodeId is empty
if (nodeId != null) {
throw APIException.badRequests.theParametersAreNotValid("cannot use node_id and node_name");
}
//get nodeIds for node names
nodeId = _coordinator.getMatchingNodeId(nodeName);
_log.info("Found node id {} for node name {}",nodeId,nodeName);
//verify nodeId found
if (nodeId == null) {
throw APIException.badRequests.parameterIsNotValid("node name");
}
}
return nodeId;
}
}