Skip to content

Commit

Permalink
Merge pull request apache#1502 from shapeblue/outofband-master
Browse files Browse the repository at this point in the history
CLOUDSTACK-9299: Out-of-band Management for CloudStackSupport access to a hosts out-of-band management interface (e.g. IPMI, iLO,
DRAC, etc.) to manage host power operations (on/off etc.) and querying current
power state in CloudStack.

Given the wide range of out-of-band management interfaces such as iLO and iDRA,
the service implementation allows for development of separate drivers as plugins.
This feature comes with a ipmitool based driver that uses the
ipmitool (http:https://linux.die.net/man/1/ipmitool) to communicate with any
out-of-band management interface that support IPMI 2.0.

This feature allows following common use-cases:
- Restarting stalled/failed hosts
- Powering off under-utilised hosts
- Powering on hosts for provisioning or to increase capacity
- Allowing system administrators to see the current power state of the host

For testing this feature, please install `ipmitool` (using yum/apt/brew) and `ipmisim`:
https://pypi.python.org/pypi/ipmisim

The default ipmitool location is assumed in /usr/bin, if this is different in your env please fix the global setting, see FS for details on various global settings.

FS:
https://cwiki.apache.org/confluence/display/CLOUDSTACK/Out-of-band+Management+for+CloudStack

/cc @jburwell @swill @abhinandanprateek @murali-reddy @borisstoyanov

* pr/1502:
  maven: ignore utils/testsmallfileinactive for rat checking
  CLOUDSTACK-9378: Fix for apache#1497
  HypervisorUtilsTest: increate timeout to 8seconds
  travis: Use patched version of ipmitool for tests
  CLOUDSTACK-9299: Out-of-band Management for CloudStack

Signed-off-by: Will Stevens <[email protected]>
  • Loading branch information
swill committed May 12, 2016
2 parents 103d62e + 12fff7d commit 143265c
Show file tree
Hide file tree
Showing 83 changed files with 5,249 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ env:
matrix:
- TESTS="smoke/test_affinity_groups smoke/test_affinity_groups_projects smoke/test_dynamicroles smoke/test_deploy_vgpu_enabled_vm smoke/test_deploy_vm_iso smoke/test_deploy_vm_root_resize smoke/test_deploy_vm_with_userdata smoke/test_deploy_vms_with_varied_deploymentplanners smoke/test_disk_offerings smoke/test_global_settings smoke/test_guest_vlan_range"
- TESTS="smoke/test_hosts smoke/test_internal_lb smoke/test_iso smoke/test_list_ids_parameter smoke/test_loadbalance smoke/test_multipleips_per_nic smoke/test_network smoke/test_network_acl smoke/test_nic smoke/test_nic_adapter_type smoke/test_non_contigiousvlan"
- TESTS="smoke/test_over_provisioning smoke/test_password_server smoke/test_portable_publicip smoke/test_primary_storage smoke/test_privategw_acl smoke/test_public_ip_range smoke/test_pvlan smoke/test_regions smoke/test_reset_vm_on_reboot smoke/test_resource_detail"
- TESTS="smoke/test_outofbandmanagement smoke/test_over_provisioning smoke/test_password_server smoke/test_portable_publicip smoke/test_primary_storage smoke/test_privategw_acl smoke/test_public_ip_range smoke/test_pvlan smoke/test_regions smoke/test_reset_vm_on_reboot smoke/test_resource_detail"
- TESTS="smoke/test_router_dhcphosts smoke/test_routers smoke/test_routers_iptables_default_policy smoke/test_routers_network_ops smoke/test_staticroles smoke/test_scale_vm smoke/test_secondary_storage smoke/test_service_offerings smoke/test_snapshots smoke/test_ssvm smoke/test_templates"
- TESTS="smoke/test_usage_events smoke/test_vm_life_cycle smoke/test_vm_snapshots smoke/test_volumes smoke/test_vpc_redundant smoke/test_vpc_router_nics smoke/test_vpc_vpn smoke/misc/test_deploy_vm smoke/misc/test_vm_ha smoke/misc/test_escalations_templates smoke/misc/test_vm_sync"

Expand Down
16 changes: 16 additions & 0 deletions api/src/com/cloud/event/EventTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,14 @@ public class EventTypes {
// Host
public static final String EVENT_HOST_RECONNECT = "HOST.RECONNECT";

// Host Out-of-band management
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_ENABLE = "HOST.OOBM.ENABLE";
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_DISABLE = "HOST.OOBM.DISABLE";
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_CONFIGURE = "HOST.OOBM.CONFIGURE";
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_ACTION = "HOST.OOBM.ACTION";
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_CHANGE_PASSWORD = "HOST.OOBM.CHANGEPASSWORD";
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_POWERSTATE_TRANSITION = "HOST.OOBM.POWERSTATE.TRANSITION";

// Maintenance
public static final String EVENT_MAINTENANCE_CANCEL = "MAINT.CANCEL";
public static final String EVENT_MAINTENANCE_CANCEL_PRIMARY_STORAGE = "MAINT.CANCEL.PS";
Expand Down Expand Up @@ -745,6 +753,14 @@ public class EventTypes {
// Host
entityEventDetails.put(EVENT_HOST_RECONNECT, Host.class);

// Host Out-of-band management
entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_ENABLE, Host.class);
entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_DISABLE, Host.class);
entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_CONFIGURE, Host.class);
entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_ACTION, Host.class);
entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_CHANGE_PASSWORD, Host.class);
entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_POWERSTATE_TRANSITION, Host.class);

// Maintenance
entityEventDetails.put(EVENT_MAINTENANCE_CANCEL, Host.class);
entityEventDetails.put(EVENT_MAINTENANCE_CANCEL_PRIMARY_STORAGE, Host.class);
Expand Down
3 changes: 3 additions & 0 deletions api/src/com/cloud/resource/ResourceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.List;

import com.cloud.dc.DataCenter;
import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
import org.apache.cloudstack.api.command.admin.host.AddHostCmd;
Expand Down Expand Up @@ -92,6 +93,8 @@ public interface ResourceService {

Cluster getCluster(Long clusterId);

DataCenter getZone(Long zoneId);

List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean forVirtualRouter, Long podId);

boolean releaseHostReservation(Long hostId);
Expand Down
1 change: 1 addition & 0 deletions api/src/org/apache/cloudstack/alert/AlertService.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ private AlertType(short type, String name, boolean isDefault) {
public static final AlertType ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED = new AlertType((short)26, "ALERT.RESOURCE.EXCEED", true);
public static final AlertType ALERT_TYPE_SYNC = new AlertType((short)27, "ALERT.TYPE.SYNC", true);
public static final AlertType ALERT_TYPE_UPLOAD_FAILED = new AlertType((short)28, "ALERT.UPLOAD.FAILED", true);
public static final AlertType ALERT_TYPE_OOBM_AUTH_ERROR = new AlertType((short)29, "ALERT.OOBM.AUTHERROR", true);

public short getType() {
return type;
Expand Down
6 changes: 5 additions & 1 deletion api/src/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class ApiConstants {
public static final String ACCOUNTS = "accounts";
public static final String ACCOUNT_TYPE = "accounttype";
public static final String ACCOUNT_ID = "accountid";
public static final String ADDRESS = "address";
public static final String ALGORITHM = "algorithm";
public static final String ALLOCATED_ONLY = "allocatedonly";
public static final String API_KEY = "apikey";
Expand Down Expand Up @@ -76,6 +77,7 @@ public class ApiConstants {
public static final String DEVICE_ID = "deviceid";
public static final String DISK_OFFERING_ID = "diskofferingid";
public static final String DISK_SIZE = "disksize";
public static final String DRIVER = "driver";
public static final String ROOT_DISK_SIZE = "rootdisksize";
public static final String DISPLAY_NAME = "displayname";
public static final String DISPLAY_NETWORK = "displaynetwork";
Expand Down Expand Up @@ -177,6 +179,8 @@ public class ApiConstants {
public static final String OS_TYPE_ID = "ostypeid";
public static final String OS_DISPLAY_NAME = "osdisplayname";
public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor";
public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate";
public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled";
public static final String PARAMS = "params";
public static final String PARENT_DOMAIN_ID = "parentdomainid";
public static final String PASSWORD = "password";
Expand All @@ -193,7 +197,7 @@ public class ApiConstants {
public static final String PORTABLE_IP_ADDRESS = "portableipaddress";
public static final String PORT_FORWARDING_SERVICE_ID = "portforwardingserviceid";
public static final String POST_URL = "postURL";
public static final String PARENT = "parent";
public static final String POWER_STATE = "powerstate";
public static final String PRIVATE_INTERFACE = "privateinterface";
public static final String PRIVATE_IP = "privateip";
public static final String PRIVATE_PORT = "privateport";
Expand Down
31 changes: 19 additions & 12 deletions api/src/org/apache/cloudstack/api/BaseResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,38 @@ public abstract class BaseResponse implements ResponseObject {
private transient String responseName;
private transient String objectName;

@SerializedName(ApiConstants.JOB_ID)
@Param(description = "the UUID of the latest async job acting on this object")
protected String jobId;

@SerializedName(ApiConstants.JOB_STATUS)
@Param(description = "the current status of the latest async job acting on this object")
private Integer jobStatus;

public BaseResponse() {
}

public BaseResponse(final String objectName) {
this.objectName = objectName;
}

@Override
public String getResponseName() {
public final String getResponseName() {
return responseName;
}

@Override
public void setResponseName(String responseName) {
public final void setResponseName(String responseName) {
this.responseName = responseName;
}

@Override
public String getObjectName() {
public final String getObjectName() {
return objectName;
}

@Override
public void setObjectName(String objectName) {
public final void setObjectName(String objectName) {
this.objectName = objectName;
}

Expand All @@ -49,14 +64,6 @@ public String getObjectId() {
return null;
}

@SerializedName(ApiConstants.JOB_ID)
@Param(description = "the UUID of the latest async job acting on this object")
protected String jobId;

@SerializedName(ApiConstants.JOB_STATUS)
@Param(description = "the current status of the latest async job acting on this object")
private Integer jobStatus;

@Override
public String getJobId() {
return jobId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,19 @@ public class ListHostsCmd extends BaseListCmd {
description = "lists hosts in the same cluster as this VM and flag hosts with enough CPU/RAm to host this VM")
private Long virtualMachineId;

@Parameter(name = ApiConstants.OUTOFBANDMANAGEMENT_ENABLED,
type = CommandType.BOOLEAN,
description = "list hosts for which out-of-band management is enabled")
private Boolean outOfBandManagementEnabled;

@Parameter(name = ApiConstants.OUTOFBANDMANAGEMENT_POWERSTATE,
type = CommandType.STRING,
description = "list hosts by its out-of-band management interface's power state. Its value can be one of [On, Off, Unknown]")
private String outOfBandManagementPowerState;

@Parameter(name = ApiConstants.RESOURCE_STATE,
type = CommandType.STRING,
description = "list hosts by resource state. Resource state represents current state determined by admin of host, valule can be one of [Enabled, Disabled, Unmanaged, PrepareForMaintenance, ErrorInMaintenance, Maintenance, Error]")
description = "list hosts by resource state. Resource state represents current state determined by admin of host, value can be one of [Enabled, Disabled, Unmanaged, PrepareForMaintenance, ErrorInMaintenance, Maintenance, Error]")
private String resourceState;

@Parameter(name = ApiConstants.DETAILS,
Expand Down Expand Up @@ -165,6 +175,15 @@ public String getResourceState() {
return resourceState;
}


public Boolean isOutOfBandManagementEnabled() {
return outOfBandManagementEnabled;
}

public String getHostOutOfBandManagementPowerState() {
return outOfBandManagementPowerState;
}

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command.admin.outofbandmanagement;

import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.host.Host;
import com.google.common.base.Strings;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.OutOfBandManagementResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagementService;

import javax.inject.Inject;

@APICommand(name = ChangeOutOfBandManagementPasswordCmd.APINAME, description = "Changes out-of-band management interface password on the host and updates the interface configuration in CloudStack if the operation succeeds, else reverts the old password",
responseObject = OutOfBandManagementResponse.class, requestHasSensitiveInfo = true, responseHasSensitiveInfo = false,
since = "4.9.0", authorized = {RoleType.Admin})
public class ChangeOutOfBandManagementPasswordCmd extends BaseAsyncCmd {
public static final String APINAME = "changeOutOfBandManagementPassword";

@Inject
private OutOfBandManagementService outOfBandManagementService;

/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////

@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true,
validations = {ApiArgValidator.PositiveNumber}, description = "the ID of the host")
private Long hostId;

@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "the new host management interface password of maximum length 16, if none is provided a random password would be used")
private String password;

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

@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());
}

CallContext.current().setEventDetails("Host Id: " + host.getId() + " Password: " + getPassword().charAt(0) + "****");
CallContext.current().putContextParameter(Host.class, host.getUuid());

final OutOfBandManagementResponse response = outOfBandManagementService.changeOutOfBandManagementPassword(host, getPassword());
response.setResponseName(getCommandName());
setResponseObject(response);
}

@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}

@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccountId();
}

public Long getHostId() {
return hostId;
}

public String getPassword() {
if (Strings.isNullOrEmpty(password)) {
password = _mgr.generateRandomPassword();
}
return password;
}

@Override
public String getEventType() {
return EventTypes.EVENT_HOST_OUTOFBAND_MANAGEMENT_CHANGE_PASSWORD;
}

@Override
public String getEventDescription() {
return "change out-of-band management password for host: " + getHostId();
}
}

0 comments on commit 143265c

Please sign in to comment.