Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.ha.HAConfigManager;
import org.apache.cloudstack.ha.HAResource;
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagementService;
import com.cloud.hypervisor.Hypervisor.HypervisorType;


import javax.inject.Inject;

Expand All @@ -50,6 +53,9 @@ public final class ConfigureHAForHostCmd extends BaseAsyncCmd {
@Inject
private HAConfigManager haConfigManager;

@Inject
private OutOfBandManagementService outOfBandManagementService;

/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -98,6 +104,12 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find host by ID: " + getHostId());
}

if (host.getHypervisorType() == HypervisorType.KVM) {
if (!outOfBandManagementService.isOutOfBandManagementEnabled(host)) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot configure HA on KVM host because Out-of-Band Management (OOBM) is not enabled.");
}
}

final boolean result = haConfigManager.configureHA(host.getId(), HAResource.ResourceType.Host, getHaProvider());
if (!result) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure HA provider for the host");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.ha.HAConfigManager;
import org.apache.cloudstack.ha.HAResource;

import com.cloud.hypervisor.Hypervisor.HypervisorType;
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagementService;
import javax.inject.Inject;

@APICommand(name = "enableHAForHost", description = "Enables HA for a host",
Expand All @@ -49,6 +50,10 @@ public final class EnableHAForHostCmd extends BaseAsyncCmd {
@Inject
private HAConfigManager haConfigManager;

@Inject
private OutOfBandManagementService outOfBandManagementService;


/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -89,6 +94,15 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE
if (host == null) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find host by ID: " + getHostId());
}

// --- YOUR NEW GUARDRAIL ---
if (host.getHypervisorType() == HypervisorType.KVM) {
if (!outOfBandManagementService.isOutOfBandManagementEnabled(host)) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot enable HA on KVM host because Out-of-Band Management (OOBM) is not enabled.");
}
}
// --------------------------

final boolean result = haConfigManager.enableHA(host.getId(), HAResource.ResourceType.Host);

CallContext.current().setEventDetails("Host Id:" + host.getId() + " HA enabled: true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import org.apache.cloudstack.api.response.OutOfBandManagementResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagementService;
import org.apache.cloudstack.ha.HAConfigManager;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import org.apache.cloudstack.ha.HAResource;

import javax.inject.Inject;

Expand All @@ -46,6 +49,9 @@
since = "4.9.0", authorized = {RoleType.Admin})
public class DisableOutOfBandManagementForHostCmd extends BaseAsyncCmd {

@Inject
private HAConfigManager haConfigManager;

@Inject
private OutOfBandManagementService outOfBandManagementService;

Expand All @@ -61,13 +67,22 @@ public class DisableOutOfBandManagementForHostCmd extends BaseAsyncCmd {
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

@Override
final public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
final Host host = _resourceService.getHost(getHostId());
if (host == null) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find host by ID: " + getHostId());
}

if (host.getHypervisorType() == HypervisorType.KVM) {
java.util.List<org.apache.cloudstack.ha.HAConfig> haConfigs = haConfigManager.listHAResources(host.getId(), HAResource.ResourceType.Host);
if (haConfigs != null && !haConfigs.isEmpty()) {
if (haConfigs.get(0).isEnabled()) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot disable Out-of-Band Management (OOBM) because HA is currently enabled on this KVM host. Please disable HA first.");
}
}
}

OutOfBandManagementResponse response = outOfBandManagementService.disableOutOfBandManagement(host);

CallContext.current().setEventDetails("Host Id:" + host.getId() + " out-of-band management enabled: false");
Expand Down
11 changes: 9 additions & 2 deletions test/integration/smoke/test_hostha_kvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def setUp(self):

self.configureAndDisableHostHa()
self.cleanup = [self.service_offering]
self.configureAndEnableOobm()

def updateConfiguration(self, name, value):
cmd = updateConfiguration.updateConfigurationCmd()
Expand Down Expand Up @@ -150,12 +151,13 @@ def getHostHaDisableCmd(self):
return cmd

def configureAndEnableHostHa(self):
#Adding sleep between configuring and enabling
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
response = self.apiclient.enableHAForHost(self.getHostHaEnableCmd())
self.assertEqual(response.haenable, True)

def configureAndDisableHostHa(self):
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
cmd = self.getHostHaDisableCmd()
cmd.hostid = self.host.id
Expand Down Expand Up @@ -229,6 +231,7 @@ def test_disable_oobm_ha_state_ineligible(self):
self.logger.debug("Starting test_disable_oobm_ha_state_ineligible")

# Enable ha for host
self.configureAndEnableOobm()
self.configureAndEnableHostHa()

# Disable OOBM
Expand All @@ -252,6 +255,7 @@ def test_hostha_configure_default_driver(self):
"""
self.logger.debug("Starting test_hostha_configure_default_driver")

self.configureAndEnableOobm()
cmd = self.getHostHaConfigCmd()
response = self.apiclient.configureHAForHost(cmd)
self.assertEqual(response.hostid, cmd.hostid)
Expand Down Expand Up @@ -343,6 +347,7 @@ def test_remove_ha_provider_not_possible(self):


# Enable HA
self.configureAndEnableOobm()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
cmd = self.getHostHaEnableCmd()
cmd.hostid = self.host.id
Expand Down Expand Up @@ -403,6 +408,7 @@ def test_hostha_kvm_host_recovering(self):
self.skipIfMSIsUnsupported()
self.configureAndStartIpmiServer()
self.assertIssueCommandState('ON', 'On')
self.configureAndEnableOobm()
self.configureAndEnableHostHa()

self.deployVM()
Expand Down Expand Up @@ -443,6 +449,7 @@ def test_hostha_kvm_host_fencing(self):
self.skipIfMSIsUnsupported()
self.configureAndStartIpmiServer()
self.assertIssueCommandState('ON', 'On')
self.configureAndEnableOobm()
self.configureAndEnableHostHa()

self.deployVM()
Expand Down
14 changes: 11 additions & 3 deletions test/integration/smoke/test_hostha_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,17 @@ def getListHostHAResources(self):
cmd = listHostHAResources.listHostHAResourcesCmd()
cmd.hostid = self.getHost().id
return cmd



def configureAndEnableHostHa(self, initialize=True):
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
response = self.apiclient.enableHAForHost(self.getHostHaEnableCmd())
self.assertEqual(response.haenable, True)
if initialize:
self.configureSimulatorHAProviderState(True, True, True, False)


def configureAndDisableHostHa(self, hostId):
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
cmd = self.getHostHaDisableCmd()
cmd.hostid = hostId
Expand Down Expand Up @@ -241,6 +241,7 @@ def test_hostha_configure_invalid_provider(self):
cmd = self.getHostHaConfigCmd()
cmd.provider = 'randomDriverThatDoesNotExist'
try:
self.setupDummyOOBM()
response = self.apiclient.configureHAForHost(cmd)
except Exception:
pass
Expand All @@ -254,6 +255,7 @@ def test_hostha_configure_default_driver(self):
Tests host-ha configuration with valid data
"""
cmd = self.getHostHaConfigCmd()
self.setupDummyOOBM()
response = self.apiclient.configureHAForHost(cmd)
self.assertEqual(response.hostid, cmd.hostid)
self.assertEqual(response.haprovider, cmd.provider.lower())
Expand Down Expand Up @@ -323,6 +325,7 @@ def test_hostha_enable_feature_valid(self):
"""
Tests host-ha enable feature with valid options
"""
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
cmd = self.getHostHaEnableCmd()
response = self.apiclient.enableHAForHost(cmd)
Expand Down Expand Up @@ -646,6 +649,7 @@ def test_configure_ha_provider_invalid(self):
"""

# Enable ha for host
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
cmd = self.getHostHaEnableCmd()
response = self.apiclient.enableHAForHost(cmd)
Expand All @@ -665,6 +669,7 @@ def test_configure_ha_provider_invalid(self):

# Call the configure HA provider API with not supported provider for HA
try:
self.setupDummyOOBM()
self.apiclient.configureHAForHost(conf_ha_cmd)
except Exception:
pass
Expand All @@ -679,6 +684,7 @@ def test_configure_ha_provider_valid(self):
"""

# Enable ha for host
self.setupDummyOOBM()
self.apiclient.configureHAForHost(self.getHostHaConfigCmd())
cmd = self.getHostHaEnableCmd()
response = self.apiclient.enableHAForHost(cmd)
Expand All @@ -698,6 +704,7 @@ def test_configure_ha_provider_valid(self):
conf_ha_cmd.hostid = cmd.hostid

# Call the configure HA provider API with not supported provider for HA
self.setupDummyOOBM()
response = self.apiclient.configureHAForHost(conf_ha_cmd)

# Check the response contains the set provider and hostID
Expand All @@ -714,6 +721,7 @@ def getHaProvider(self, host):

def configureHaProvider(self):
cmd = self.getHostHaConfigCmd(self.getHaProvider(self.getHost()))
self.setupDummyOOBM()
return self.apiclient.configureHAForHost(cmd)


Expand Down
21 changes: 21 additions & 0 deletions tools/marvin/marvin/cloudstackTestCase.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ def tearDownClass(cls):
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)

def setupDummyOOBM(self):
try:
from apache.cloudstack.api.command.admin import outOfBandManagement
except ImportError:
self.debug("OOBM module not available, skipping dummy setup.")
return

conf_cls = outOfBandManagement.configureOutOfBandManagementForHost
oobm_cmd = conf_cls.configureOutOfBandManagementForHostCmd()
oobm_cmd.hostid = self.host.id
oobm_cmd.address = "10.1.1.1"
oobm_cmd.driver = "ipmitool"
oobm_cmd.username = "admin"
oobm_cmd.password = "password"
self.apiclient.configureOutOfBandManagementForHost(oobm_cmd)

en_cls = outOfBandManagement.enableOutOfBandManagementForHost
enable_oobm_cmd = en_cls.enableOutOfBandManagementForHostCmd()
enable_oobm_cmd.hostid = self.host.id
self.apiclient.enableOutOfBandManagementForHost(enable_oobm_cmd)

def tearDown(self):
self.debug("Cleaning up the resources")
try:
Expand Down
3 changes: 3 additions & 0 deletions ui/src/config/section/infra/hosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ export default {
docHelp: 'adminguide/hosts.html#out-of-band-management',
dataView: true,
show: (record) => {
if (record.hypervisor === 'KVM' && record?.hostha?.haenable === true) {
return false
}
return record?.outofbandmanagement?.enabled === true
},
args: ['hostid'],
Expand Down
Loading