mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Add a API to allow adding external hypervisor cluster
This commit is contained in:
parent
1e595ddf00
commit
65c094a2c0
102
api/src/com/cloud/api/commands/AddExternalClusterCmd.java
Normal file
102
api/src/com/cloud/api/commands/AddExternalClusterCmd.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package com.cloud.api.commands;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.cloud.api.ApiConstants;
|
||||||
|
import com.cloud.api.BaseCmd;
|
||||||
|
import com.cloud.api.Implementation;
|
||||||
|
import com.cloud.api.Parameter;
|
||||||
|
import com.cloud.api.ServerApiException;
|
||||||
|
import com.cloud.api.response.HostResponse;
|
||||||
|
import com.cloud.api.response.ListResponse;
|
||||||
|
import com.cloud.exception.DiscoveryException;
|
||||||
|
import com.cloud.host.Host;
|
||||||
|
|
||||||
|
@Implementation(description="Adds a new external cluster", responseObject=HostResponse.class)
|
||||||
|
public class AddExternalClusterCmd extends BaseCmd {
|
||||||
|
public static final Logger s_logger = Logger.getLogger(AddExternalClusterCmd.class.getName());
|
||||||
|
|
||||||
|
private static final String s_name = "addexternalclusterresponse";
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.CLUSTER_NAME, type=CommandType.STRING, description="the cluster name")
|
||||||
|
private String clusterName;
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required=true, description="the password for the host")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.POD_ID, type=CommandType.LONG, description="the Pod ID for the host")
|
||||||
|
private Long podId;
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.URL, type=CommandType.STRING, required=true, description="the host URL")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required=true, description="the username for the host")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="the Zone ID for the host")
|
||||||
|
private Long zoneId;
|
||||||
|
|
||||||
|
@Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, required=false, description="hypervisor type of the host")
|
||||||
|
private String hypervisor;
|
||||||
|
|
||||||
|
public String getClusterName() {
|
||||||
|
return clusterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getPodId() {
|
||||||
|
return podId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getZoneId() {
|
||||||
|
return zoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHypervisor() {
|
||||||
|
return hypervisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return s_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(){
|
||||||
|
try {
|
||||||
|
List<? extends Host> result = _resourceService.discoverExternalCluster(this);
|
||||||
|
ListResponse<HostResponse> response = new ListResponse<HostResponse>();
|
||||||
|
List<HostResponse> hostResponses = new ArrayList<HostResponse>();
|
||||||
|
if (result != null) {
|
||||||
|
for (Host host : result) {
|
||||||
|
HostResponse hostResponse = _responseGenerator.createHostResponse(host);
|
||||||
|
hostResponses.add(hostResponse);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add external host cluster");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setResponses(hostResponses);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
|
||||||
|
this.setResponseObject(response);
|
||||||
|
} catch (DiscoveryException ex) {
|
||||||
|
s_logger.warn("Exception: ", ex);
|
||||||
|
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,11 +3,20 @@
|
|||||||
*/
|
*/
|
||||||
package com.cloud.org;
|
package com.cloud.org;
|
||||||
|
|
||||||
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
|
|
||||||
public interface Cluster extends Grouping {
|
public interface Cluster extends Grouping {
|
||||||
|
public static enum ClusterType {
|
||||||
|
CloudManaged,
|
||||||
|
ExternalManaged;
|
||||||
|
};
|
||||||
|
|
||||||
long getId();
|
long getId();
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
long getDataCenterId();
|
long getDataCenterId();
|
||||||
long getPodId();
|
long getPodId();
|
||||||
|
|
||||||
|
HypervisorType getHypervisorType();
|
||||||
|
ClusterType getClusterType();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package com.cloud.resource;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.cloud.api.commands.AddExternalClusterCmd;
|
||||||
import com.cloud.api.commands.AddHostCmd;
|
import com.cloud.api.commands.AddHostCmd;
|
||||||
import com.cloud.api.commands.AddSecondaryStorageCmd;
|
import com.cloud.api.commands.AddSecondaryStorageCmd;
|
||||||
import com.cloud.api.commands.CancelMaintenanceCmd;
|
import com.cloud.api.commands.CancelMaintenanceCmd;
|
||||||
@ -43,6 +44,19 @@ public interface ResourceService {
|
|||||||
Host cancelMaintenance(CancelMaintenanceCmd cmd) throws InvalidParameterValueException;
|
Host cancelMaintenance(CancelMaintenanceCmd cmd) throws InvalidParameterValueException;
|
||||||
|
|
||||||
Host reconnectHost(ReconnectHostCmd cmd) throws AgentUnavailableException;
|
Host reconnectHost(ReconnectHostCmd cmd) throws AgentUnavailableException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We will automatically create a cloud.com cluster to attach to the external cluster and return a hyper host to perform
|
||||||
|
* host related operation within the cluster
|
||||||
|
*
|
||||||
|
* @param cmd
|
||||||
|
* @return
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* @throws DiscoveryException
|
||||||
|
* @throws InvalidParameterValueException
|
||||||
|
*/
|
||||||
|
List<? extends Host> discoverExternalCluster(AddExternalClusterCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
|
||||||
|
|
||||||
List<? extends Host> discoverHosts(AddHostCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
|
List<? extends Host> discoverHosts(AddHostCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
|
||||||
List<? extends Host> discoverHosts(AddSecondaryStorageCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
|
List<? extends Host> discoverHosts(AddSecondaryStorageCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
|
||||||
Host maintain(PrepareForMaintenanceCmd cmd) throws InvalidParameterValueException;
|
Host maintain(PrepareForMaintenanceCmd cmd) throws InvalidParameterValueException;
|
||||||
|
|||||||
@ -73,6 +73,7 @@ import com.cloud.agent.transport.Response;
|
|||||||
import com.cloud.alert.AlertManager;
|
import com.cloud.alert.AlertManager;
|
||||||
import com.cloud.api.BaseCmd;
|
import com.cloud.api.BaseCmd;
|
||||||
import com.cloud.api.ServerApiException;
|
import com.cloud.api.ServerApiException;
|
||||||
|
import com.cloud.api.commands.AddExternalClusterCmd;
|
||||||
import com.cloud.api.commands.AddHostCmd;
|
import com.cloud.api.commands.AddHostCmd;
|
||||||
import com.cloud.api.commands.AddSecondaryStorageCmd;
|
import com.cloud.api.commands.AddSecondaryStorageCmd;
|
||||||
import com.cloud.api.commands.CancelMaintenanceCmd;
|
import com.cloud.api.commands.CancelMaintenanceCmd;
|
||||||
@ -127,6 +128,7 @@ import com.cloud.network.IPAddressVO;
|
|||||||
import com.cloud.network.NetworkManager;
|
import com.cloud.network.NetworkManager;
|
||||||
import com.cloud.network.dao.IPAddressDao;
|
import com.cloud.network.dao.IPAddressDao;
|
||||||
import com.cloud.offering.ServiceOffering;
|
import com.cloud.offering.ServiceOffering;
|
||||||
|
import com.cloud.org.Cluster;
|
||||||
import com.cloud.resource.Discoverer;
|
import com.cloud.resource.Discoverer;
|
||||||
import com.cloud.resource.ResourceService;
|
import com.cloud.resource.ResourceService;
|
||||||
import com.cloud.resource.ServerResource;
|
import com.cloud.resource.ServerResource;
|
||||||
@ -524,8 +526,6 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, ResourceS
|
|||||||
return pcs;
|
return pcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected AgentAttache handleDirectConnect(ServerResource resource, StartupCommand[] startup, Map<String, String> details, boolean old) throws ConnectionException {
|
protected AgentAttache handleDirectConnect(ServerResource resource, StartupCommand[] startup, Map<String, String> details, boolean old) throws ConnectionException {
|
||||||
if (startup == null) {
|
if (startup == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -544,6 +544,118 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, ResourceS
|
|||||||
return attache;
|
return attache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends Host> discoverExternalCluster(AddExternalClusterCmd cmd)
|
||||||
|
throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException {
|
||||||
|
Long dcId = cmd.getZoneId();
|
||||||
|
Long podId = cmd.getPodId();
|
||||||
|
String clusterName = cmd.getClusterName();
|
||||||
|
String url = cmd.getUrl();
|
||||||
|
String username = cmd.getUsername();
|
||||||
|
String password = cmd.getPassword();
|
||||||
|
|
||||||
|
URI uri = null;
|
||||||
|
|
||||||
|
//Check if the zone exists in the system
|
||||||
|
if (_dcDao.findById(dcId) == null ){
|
||||||
|
throw new InvalidParameterValueException("Can't find zone by id " + dcId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the pod exists in the system
|
||||||
|
if (podId != null) {
|
||||||
|
if (_podDao.findById(podId) == null ){
|
||||||
|
throw new InvalidParameterValueException("Can't find pod by id " + podId);
|
||||||
|
}
|
||||||
|
//check if pod belongs to the zone
|
||||||
|
HostPodVO pod = _podDao.findById(podId);
|
||||||
|
if (!Long.valueOf(pod.getDataCenterId()).equals(dcId)) {
|
||||||
|
throw new InvalidParameterValueException("Pod " + podId + " doesn't belong to the zone " + dcId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify cluster information and create a new cluster if needed
|
||||||
|
if (clusterName == null || clusterName.isEmpty()) {
|
||||||
|
throw new InvalidParameterValueException("Please specify cluster name");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cmd.getHypervisor() == null || cmd.getHypervisor().isEmpty()) {
|
||||||
|
throw new InvalidParameterValueException("Please specify a hypervisor");
|
||||||
|
}
|
||||||
|
|
||||||
|
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.valueOf(cmd.getHypervisor());
|
||||||
|
if(hypervisorType == null) {
|
||||||
|
throw new InvalidParameterValueException("Please specify a valid hypervisor name");
|
||||||
|
}
|
||||||
|
|
||||||
|
Discoverer discoverer = getMatchingDiscover(cmd.getHypervisor());
|
||||||
|
if(discoverer == null) {
|
||||||
|
throw new InvalidParameterValueException("Please specify a valid hypervisor");
|
||||||
|
}
|
||||||
|
|
||||||
|
long clusterId = 0;
|
||||||
|
if (clusterName != null) {
|
||||||
|
ClusterVO cluster = new ClusterVO(dcId, podId, clusterName);
|
||||||
|
cluster.setHypervisorType(cmd.getHypervisor());
|
||||||
|
cluster.setClusterType(Cluster.ClusterType.ExternalManaged);
|
||||||
|
try {
|
||||||
|
cluster = _clusterDao.persist(cluster);
|
||||||
|
} catch (Exception e) {
|
||||||
|
cluster = _clusterDao.findBy(clusterName, podId);
|
||||||
|
if (cluster == null) {
|
||||||
|
throw new CloudRuntimeException("Unable to create cluster " + clusterName + " in pod " + podId + " and data center " + dcId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clusterId = cluster.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
uri = new URI(UriUtils.encodeURIComponent(url));
|
||||||
|
if (uri.getScheme() == null) {
|
||||||
|
throw new InvalidParameterValueException("uri.scheme is null " + url + ", add http:// as a prefix");
|
||||||
|
} else if (uri.getScheme().equalsIgnoreCase("http")) {
|
||||||
|
if (uri.getHost() == null || uri.getHost().equalsIgnoreCase("") || uri.getPath() == null || uri.getPath().equalsIgnoreCase("")) {
|
||||||
|
throw new InvalidParameterValueException("Your host and/or path is wrong. Make sure it's of the format http://hostname/path");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new InvalidParameterValueException(url + " is not a valid uri");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HostVO> hosts = new ArrayList<HostVO>();
|
||||||
|
Map<? extends ServerResource, Map<String, String>> resources = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
resources = discoverer.find(dcId, podId, clusterId, uri, username, password);
|
||||||
|
} catch(Exception e) {
|
||||||
|
s_logger.info("Exception in external cluster discovery process with discoverer: " + discoverer.getName());
|
||||||
|
}
|
||||||
|
if (resources != null) {
|
||||||
|
for (Map.Entry<? extends ServerResource, Map<String, String>> entry : resources.entrySet()) {
|
||||||
|
ServerResource resource = entry.getKey();
|
||||||
|
AgentAttache attache = simulateStart(resource, entry.getValue(), true);
|
||||||
|
if (attache != null) {
|
||||||
|
hosts.add(_hostDao.findById(attache.getId()));
|
||||||
|
}
|
||||||
|
discoverer.postDiscovery(hosts, _nodeId);
|
||||||
|
}
|
||||||
|
s_logger.info("External cluster has been successfully discovered by " + discoverer.getName());
|
||||||
|
return hosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_logger.warn("Unable to find the server resources at " + url);
|
||||||
|
throw new DiscoveryException("Unable to add the external cluster");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Discoverer getMatchingDiscover(String hypervisorType) {
|
||||||
|
Enumeration<Discoverer> en = _discoverers.enumeration();
|
||||||
|
while (en.hasMoreElements()) {
|
||||||
|
Discoverer discoverer = en.nextElement();
|
||||||
|
if(discoverer.matchHypervisor(hypervisorType))
|
||||||
|
return discoverer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<? extends Host> discoverHosts(AddHostCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException {
|
public List<? extends Host> discoverHosts(AddHostCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException {
|
||||||
Long dcId = cmd.getZoneId();
|
Long dcId = cmd.getZoneId();
|
||||||
|
|||||||
@ -19,6 +19,8 @@ package com.cloud.dc;
|
|||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
@ -49,14 +51,19 @@ public class ClusterVO implements Cluster {
|
|||||||
@Column(name="hypervisor_type")
|
@Column(name="hypervisor_type")
|
||||||
String hypervisorType;
|
String hypervisorType;
|
||||||
|
|
||||||
|
@Column(name="cluster_type")
|
||||||
|
@Enumerated(value=EnumType.STRING)
|
||||||
|
Cluster.ClusterType clusterType;
|
||||||
|
|
||||||
public ClusterVO() {
|
public ClusterVO() {
|
||||||
|
clusterType = Cluster.ClusterType.CloudManaged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterVO(long dataCenterId, long podId, String name) {
|
public ClusterVO(long dataCenterId, long podId, String name) {
|
||||||
this.dataCenterId = dataCenterId;
|
this.dataCenterId = dataCenterId;
|
||||||
this.podId = podId;
|
this.podId = podId;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.clusterType = Cluster.ClusterType.CloudManaged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
@ -75,6 +82,14 @@ public class ClusterVO implements Cluster {
|
|||||||
return podId;
|
return podId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cluster.ClusterType getClusterType() {
|
||||||
|
return clusterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClusterType(Cluster.ClusterType clusterType) {
|
||||||
|
this.clusterType = clusterType;
|
||||||
|
}
|
||||||
|
|
||||||
public ClusterVO(long clusterId) {
|
public ClusterVO(long clusterId) {
|
||||||
this.id = clusterId;
|
this.id = clusterId;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -219,6 +219,7 @@ CREATE TABLE `cloud`.`cluster` (
|
|||||||
`pod_id` bigint unsigned NOT NULL COMMENT 'pod id',
|
`pod_id` bigint unsigned NOT NULL COMMENT 'pod id',
|
||||||
`data_center_id` bigint unsigned NOT NULL COMMENT 'data center id',
|
`data_center_id` bigint unsigned NOT NULL COMMENT 'data center id',
|
||||||
`hypervisor_type` varchar(255),
|
`hypervisor_type` varchar(255),
|
||||||
|
`cluster_type` varchar(64) DEFAULT 'CloudManaged',
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user