Merge branch 'ucs' into javelin

Conflicts:
	api/src/org/apache/cloudstack/api/ApiConstants.java
	plugins/pom.xml
	utils/src/com/cloud/utils/exception/CloudRuntimeException.java
	utils/src/com/cloud/utils/exception/RuntimeCloudException.java
This commit is contained in:
frank 2013-01-31 14:44:07 -08:00
commit 8416d81b99
29 changed files with 1661 additions and 2 deletions

View File

@ -428,6 +428,7 @@ public class ApiConstants {
public static final String COUNTERPARAM_LIST = "counterparam";
public static final String AUTOSCALE_USER_ID = "autoscaleuserid";
public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername";
public static final String UCS_DN = "ucsdn";
public enum HostDetails {
all, capacity, events, stats, min;

37
plugins/hypervisors/ucs/pom.xml Executable file
View File

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloudstack-plugins</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-hypervisor-ucs</artifactId>
<version>4.1.0-SNAPSHOT</version>
<name>cloud-plugin-hypervisor-ucs</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,7 @@
package com.cloud.ucs.database;
import com.cloud.utils.db.GenericDao;
public interface UcsBladeDao extends GenericDao<UcsBladeVO, Long> {
}

View File

@ -0,0 +1,11 @@
package com.cloud.ucs.database;
import javax.ejb.Local;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GenericDaoBase;
@Local(value = { UcsBladeDao.class })
@DB(txn = false)
public class UcsBladeDaoImpl extends GenericDaoBase<UcsBladeVO, Long> implements UcsBladeDao {
}

View File

@ -0,0 +1,69 @@
package com.cloud.ucs.database;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="ucs_blade")
public class UcsBladeVO {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private long id;
@Column(name="uuid")
private String uuid;
@Column(name="ucs_manager_id")
private long ucsManagerId;
@Column(name="host_id")
private Long hostId;
@Column(name="dn")
private String dn;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getUcsManagerId() {
return ucsManagerId;
}
public void setUcsManagerId(long ucsManagerId) {
this.ucsManagerId = ucsManagerId;
}
public Long getHostId() {
return hostId;
}
public void setHostId(Long hostId) {
this.hostId = hostId;
}
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
}

View File

@ -0,0 +1,16 @@
package com.cloud.ucs.database;
import java.util.List;
import java.util.Map;
import javax.naming.ConfigurationException;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria2;
public interface UcsManagerDao extends GenericDao<UcsManagerVO, Long> {
}

View File

@ -0,0 +1,12 @@
package com.cloud.ucs.database;
import javax.ejb.Local;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GenericDaoBase;
@Local(value = { UcsManagerDao.class })
@DB(txn = false)
public class UcsManagerDaoImpl extends GenericDaoBase<UcsManagerVO, Long> implements UcsManagerDao {
}

View File

@ -0,0 +1,78 @@
package com.cloud.ucs.database;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="ucs_manager")
public class UcsManagerVO {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private long id;
@Column(name="zone_id")
private long zoneId;
@Column(name="uuid")
private String uuid;
@Column(name="name")
private String name;
@Column(name="url")
private String url;
@Column(name="username")
private String username;
@Column(name="password")
private String password;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getZoneId() {
return zoneId;
}
public void setZoneId(long zoneId) {
this.zoneId = zoneId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,108 @@
package com.cloud.ucs.manager;
import javax.inject.Inject;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.log4j.Logger;
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.server.ManagementService;
import com.cloud.user.Account;
@APICommand(description="Adds a Ucs manager", responseObject=AddUcsManagerResponse.class)
public class AddUcsManagerCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(AddUcsManagerCmd.class);
@Inject
private UcsManager mgr;
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, description="the Zone id for the ucs manager", required=true)
private Long zoneId;
@Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="the name of UCS manager")
private String name;
@Parameter(name=ApiConstants.URL, type=CommandType.STRING, description="the name of UCS url")
private String url;
@Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, description="the username of UCS")
private String username;
@Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="the password of UCS")
private String password;
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException {
try {
AddUcsManagerResponse rsp = mgr.addUcsManager(this);
rsp.setObjectName("ucsmanager");
rsp.setResponseName(getCommandName());
this.setResponseObject(rsp);
} catch (Exception e) {
s_logger.warn("Exception: ", e);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public String getCommandName() {
return "addUcsManagerResponse";
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
public Long getZoneId() {
return zoneId;
}
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,53 @@
package com.cloud.ucs.manager;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class AddUcsManagerResponse extends BaseResponse {
@SerializedName(ApiConstants.ID) @Param(description="the ID of the ucs manager")
private String id;
@SerializedName(ApiConstants.NAME) @Param(description="the name of ucs manager")
private String name;
@SerializedName(ApiConstants.URL) @Param(description="the url of ucs manager")
private String url;
@SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID of ucs manager")
private String zoneId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getZoneId() {
return zoneId;
}
public void setZoneId(String zoneId) {
this.zoneId = zoneId;
}
}

View File

@ -0,0 +1,76 @@
package com.cloud.ucs.manager;
import javax.inject.Inject;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.log4j.Logger;
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.user.Account;
@APICommand(description="associate a profile to a blade", responseObject=AssociateUcsProfileToBladesInClusterResponse.class)
public class AssociateUcsProfileToBladeCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(AssociateUcsProfileToBladeCmd.class);
@Inject
private UcsManager mgr;
private Long ucsManagerId;
private String profileDn;
private Long bladeId;
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException {
try {
mgr.associateProfileToBlade(this);
AssociateUcsProfileToBladesInClusterResponse rsp = new AssociateUcsProfileToBladesInClusterResponse();
rsp.setResponseName(getCommandName());
rsp.setObjectName("associateucsprofiletobalde");
this.setResponseObject(rsp);
} catch (Exception e) {
s_logger.warn("Exception: ", e);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public String getCommandName() {
return "associateucsprofiletobladeresponse";
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
public Long getUcsManagerId() {
return ucsManagerId;
}
public void setUcsManagerId(Long ucsManagerId) {
this.ucsManagerId = ucsManagerId;
}
public String getProfileDn() {
return profileDn;
}
public void setProfileDn(String profileDn) {
this.profileDn = profileDn;
}
public Long getBladeId() {
return bladeId;
}
public void setBladeId(Long bladeId) {
this.bladeId = bladeId;
}
}

View File

@ -0,0 +1,6 @@
package com.cloud.ucs.manager;
import org.apache.cloudstack.api.BaseResponse;
public class AssociateUcsProfileToBladesInClusterResponse extends BaseResponse {
}

View File

@ -0,0 +1,62 @@
package com.cloud.ucs.manager;
import javax.inject.Inject;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.log4j.Logger;
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.server.ManagementService;
import com.cloud.user.Account;
@APICommand(description="List ucs manager", responseObject=ListUcsManagerResponse.class)
public class ListUcsManagerCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(ListUcsManagerCmd.class);
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, description="the zone id", required=true)
private Long zoneId;
@Inject
private UcsManager mgr;
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException {
try {
ListResponse<ListUcsManagerResponse> response = mgr.listUcsManager(this);
response.setResponseName(getCommandName());
response.setObjectName("ucsmanager");
this.setResponseObject(response);
} catch (Exception e) {
s_logger.warn("Exception: ", e);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public String getCommandName() {
return "listucsmanagerreponse";
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
public Long getZoneId() {
return zoneId;
}
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
}

View File

@ -0,0 +1,42 @@
package com.cloud.ucs.manager;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class ListUcsManagerResponse extends BaseResponse {
@SerializedName(ApiConstants.ID) @Param(description="id of ucs manager")
private String id;
@SerializedName(ApiConstants.NAME) @Param(description="name of ucs manager")
private String name;
@SerializedName(ApiConstants.ZONE_ID) @Param(description="zone id the ucs manager belongs to")
private String zoneId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getZoneId() {
return zoneId;
}
public void setZoneId(String zoneId) {
this.zoneId = zoneId;
}
}

View File

@ -0,0 +1,63 @@
package com.cloud.ucs.manager;
import javax.inject.Inject;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.log4j.Logger;
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.server.ManagementService;
import com.cloud.user.Account;
@APICommand(description="List profile in ucs manager", responseObject=ListUcsProfileResponse.class)
public class ListUcsProfileCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(ListUcsProfileCmd.class);
@Inject UcsManager mgr;
@Parameter(name=ApiConstants.ID, type=CommandType.LONG, description="the id for the ucs manager", required=true)
private Long ucsManagerId;
public Long getUcsManagerId() {
return ucsManagerId;
}
public void setUcsManagerId(Long ucsManagerId) {
this.ucsManagerId = ucsManagerId;
}
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException {
try {
ListResponse<ListUcsProfileResponse> response = mgr.listUcsProfiles(this);
response.setResponseName(getCommandName());
response.setObjectName("ucsprofile");
this.setResponseObject(response);
} catch (Exception e) {
s_logger.warn("Exception: ", e);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public String getCommandName() {
return "listucsprofileresponse";
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -0,0 +1,20 @@
package com.cloud.ucs.manager;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class ListUcsProfileResponse extends BaseResponse {
@SerializedName(ApiConstants.UCS_DN) @Param(description="the dn of ucs profile")
private String dn;
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
}

View File

@ -0,0 +1,22 @@
package com.cloud.ucs.manager;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringTemplate {
public static String replaceTokens(String text, Map<String, String> replacements) {
Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(text);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String replacement = replacements.get(matcher.group(1));
if (replacement != null) {
matcher.appendReplacement(buffer, "");
buffer.append(replacement);
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
}

View File

@ -0,0 +1,83 @@
package com.cloud.ucs.manager;
import com.cloud.utils.xmlobject.XmlObject;
public class UcsCommands {
public static String loginCmd(String username, String password) {
XmlObject cmd = new XmlObject("aaaLogin");
cmd.putElement("inName", username);
cmd.putElement("inPassword", password);
return cmd.dump();
}
public static String listComputeBlades(String cookie) {
XmlObject cmd = new XmlObject("configResolveClass");
cmd.putElement("classId", "computeBlade");
cmd.putElement("cookie", cookie);
cmd.putElement("inHierarchical", "false");
return cmd.dump();
}
public static String listProfiles(String cookie) {
XmlObject cmd = new XmlObject("configFindDnsByClassId");
cmd.putElement("classId", "lsServer");
cmd.putElement("cookie", cookie);
return cmd.dump();
}
public static String cloneProfile(String cookie, String srcDn, String newProfileName) {
XmlObject cmd = new XmlObject("lsClone");
cmd.putElement("cookie", cookie);
cmd.putElement("dn", srcDn);
cmd.putElement("inTargetOrg", "org-root");
cmd.putElement("inServerName", newProfileName);
cmd.putElement("inHierarchical", "false");
return cmd.dump();
}
public static String configResolveDn(String cookie, String dn) {
XmlObject cmd = new XmlObject("configResolveDn");
cmd.putElement("cookie", cookie);
cmd.putElement("dn", dn);
return cmd.toString();
}
public static String associateProfileToBlade(String cookie, String profileDn, String bladeDn) {
XmlObject cmd = new XmlObject("configConfMos").putElement("inHierarchical", "true").putElement(
"inConfigs", new XmlObject("inConfigs").putElement(
"pair", new XmlObject("pair").putElement("key", profileDn).putElement(
"lsServer", new XmlObject("lsServer")
.putElement("agentPolicyName", "")
.putElement("biosProfileName", "")
.putElement("bootPolicyName", "")
.putElement("descr", "")
.putElement("dn", profileDn)
.putElement("dynamicConPolicyName", "")
.putElement("extIPState", "none")
.putElement("hostFwPolicyName", "")
.putElement("identPoolName", "")
.putElement("localDiskPolicyName", "")
.putElement("maintPolicyName", "")
.putElement("mgmtAccessPolicyName", "")
.putElement("mgmtFwPolicyName", "")
.putElement("powerPolicyName", "")
.putElement("scrubPolicyName", "")
.putElement("solPolicyName", "")
.putElement("srcTemplName", "")
.putElement("statsPolicyName", "default")
.putElement("status", "")
.putElement("usrLbl", "")
.putElement("", "")
.putElement("vconProfileName", "")
.putElement("lsBinding", new XmlObject("lsBinding")
.putElement("pnDn", bladeDn)
.putElement("restrictMigration", "no")
.putElement("rn", "pn")
)
)
)
);
return cmd.dump();
}
}

View File

@ -0,0 +1,33 @@
package com.cloud.ucs.manager;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import com.cloud.utils.exception.CloudRuntimeException;
public class UcsHttpClient {
private static HttpClient client = new HttpClient();
private String url;
public UcsHttpClient(String ip) {
this.url = String.format("http://%s/nuova", ip);
}
public String call(String xml) {
PostMethod post = new PostMethod(url);
post.setRequestEntity(new StringRequestEntity(xml));
post.setRequestHeader("Content-type", "text/xml");
try {
int result = client.executeMethod(post);
if (result != 200) {
throw new CloudRuntimeException("Call failed: " + post.getResponseBodyAsString());
}
return post.getResponseBodyAsString();
} catch (Exception e) {
throw new CloudRuntimeException(e.getMessage(), e);
} finally {
post.releaseConnection();
}
}
}

View File

@ -0,0 +1,15 @@
package com.cloud.ucs.manager;
import org.apache.cloudstack.api.response.ListResponse;
import com.cloud.utils.component.Manager;
public interface UcsManager extends Manager {
AddUcsManagerResponse addUcsManager(AddUcsManagerCmd cmd);
ListResponse<ListUcsProfileResponse> listUcsProfiles(ListUcsProfileCmd cmd);
ListResponse<ListUcsManagerResponse> listUcsManager(ListUcsManagerCmd cmd);
void associateProfileToBlade(AssociateUcsProfileToBladeCmd cmd);
}

View File

@ -0,0 +1,288 @@
package com.cloud.ucs.manager;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ResponseGenerator;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cxf.helpers.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.host.HostVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.org.Cluster;
import com.cloud.resource.ResourceService;
import com.cloud.ucs.database.UcsBladeDao;
import com.cloud.ucs.database.UcsBladeVO;
import com.cloud.ucs.database.UcsManagerDao;
import com.cloud.ucs.database.UcsManagerVO;
import com.cloud.ucs.structure.ComputeBlade;
import com.cloud.ucs.structure.UcsProfile;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.SearchCriteria2;
import com.cloud.utils.db.SearchCriteriaService;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import com.cloud.utils.xmlobject.XmlObject;
import com.cloud.utils.xmlobject.XmlObjectParser;
@Local(value = { UcsManager.class })
@Component
public class UcsManagerImpl implements UcsManager {
public static final Logger s_logger = Logger.getLogger(UcsManagerImpl.class);
@Inject
private UcsManagerDao ucsDao;
@Inject
private ResourceService resourceService;
@Inject
private ClusterDao clusterDao;
@Inject
private ClusterDetailsDao clusterDetailsDao;
@Inject
private UcsBladeDao bladeDao;
private Map<Long, String> cookies = new HashMap<Long, String>();
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
return true;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return "UcsManager";
}
private void discoverBlades(UcsManagerVO ucsMgrVo) {
List<ComputeBlade> blades = listBlades(ucsMgrVo.getId());
for (ComputeBlade b : blades) {
UcsBladeVO vo = new UcsBladeVO();
vo.setDn(b.getDn());
vo.setUcsManagerId(ucsMgrVo.getId());
vo.setUuid(UUID.randomUUID().toString());
bladeDao.persist(vo);
}
}
@Override
@DB
public AddUcsManagerResponse addUcsManager(AddUcsManagerCmd cmd) {
UcsManagerVO vo = new UcsManagerVO();
vo.setUuid(UUID.randomUUID().toString());
vo.setPassword(cmd.getPassword());
vo.setUrl(cmd.getUrl());
vo.setUsername(cmd.getUsername());
vo.setZoneId(cmd.getZoneId());
vo.setName(cmd.getName());
Transaction txn = Transaction.currentTxn();
txn.start();
ucsDao.persist(vo);
txn.commit();
AddUcsManagerResponse rsp = new AddUcsManagerResponse();
rsp.setId(String.valueOf(vo.getId()));
rsp.setName(vo.getName());
rsp.setUrl(vo.getUrl());
rsp.setZoneId(String.valueOf(vo.getZoneId()));
discoverBlades(vo);
return rsp;
}
private String getCookie(Long ucsMgrId) {
try {
String cookie = cookies.get(ucsMgrId);
if (cookie == null) {
UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId);
UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl());
String login = UcsCommands.loginCmd(mgrvo.getUsername(), mgrvo.getPassword());
cookie = client.call(login);
cookies.put(ucsMgrId, cookie);
}
return cookie;
} catch (Exception e) {
throw new CloudRuntimeException("Cannot get cookie", e);
}
}
private List<ComputeBlade> listBlades(Long ucsMgrId) {
String cookie = getCookie(ucsMgrId);
UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId);
UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl());
String cmd = UcsCommands.listComputeBlades(cookie);
String ret = client.call(cmd);
return ComputeBlade.fromXmString(ret);
}
private List<UcsProfile> getUcsProfiles(Long ucsMgrId) {
String cookie = getCookie(ucsMgrId);
UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId);
String cmd = UcsCommands.listProfiles(cookie);
UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl());
String res = client.call(cmd);
List<UcsProfile> profiles = UcsProfile.fromXmlString(res);
return profiles;
}
@Override
public ListResponse<ListUcsProfileResponse> listUcsProfiles(ListUcsProfileCmd cmd) {
List<UcsProfile> profiles = getUcsProfiles(cmd.getUcsManagerId());
ListResponse<ListUcsProfileResponse> response = new ListResponse<ListUcsProfileResponse>();
List<ListUcsProfileResponse> rs = new ArrayList<ListUcsProfileResponse>();
for (UcsProfile p : profiles) {
ListUcsProfileResponse r = new ListUcsProfileResponse();
r.setObjectName("ucsprofile");
r.setDn(p.getDn());
rs.add(r);
}
response.setResponses(rs);
return response;
}
private String cloneProfile(Long ucsMgrId, String srcDn, String newProfileName) {
UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId);
UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl());
String cookie = getCookie(ucsMgrId);
String cmd = UcsCommands.cloneProfile(cookie, srcDn, newProfileName);
String res = client.call(cmd);
XmlObject xo = XmlObjectParser.parseFromString(res);
return xo.get("lsClone.outConfig.lsServer.dn");
}
private boolean isProfileAssociated(Long ucsMgrId, String dn) {
UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId);
UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl());
String cookie = getCookie(ucsMgrId);
String cmd = UcsCommands.configResolveDn(cookie, dn);
String res = client.call(cmd);
XmlObject xo = XmlObjectParser.parseFromString(res);
return xo.get("outConfig.lsServer.assocState").equals("associated");
}
@Override
public void associateProfileToBlade(AssociateUcsProfileToBladeCmd cmd) {
SearchCriteriaService<UcsBladeVO, UcsBladeVO> q = SearchCriteria2.create(UcsBladeVO.class);
q.addAnd(q.getEntity().getUcsManagerId(), Op.EQ, cmd.getUcsManagerId());
q.addAnd(q.getEntity().getId(), Op.EQ, cmd.getBladeId());
UcsBladeVO bvo = q.find();
if (bvo == null) {
throw new IllegalArgumentException(String.format("cannot find UCS blade[id:%s, ucs manager id:%s]", cmd.getBladeId(), cmd.getUcsManagerId()));
}
if (bvo.getHostId() != null) {
throw new CloudRuntimeException(String.format("blade[id:%s, dn:%s] has been associated with host[id:%s]", bvo.getId(), bvo.getDn(), bvo.getHostId()));
}
UcsManagerVO mgrvo = ucsDao.findById(cmd.getUcsManagerId());
String cookie = getCookie(cmd.getUcsManagerId());
String pdn = cloneProfile(mgrvo.getId(), cmd.getProfileDn(), "profile-for-blade-" + bvo.getId());
String ucscmd = UcsCommands.associateProfileToBlade(cookie, pdn, bvo.getDn());
UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl());
String res = client.call(ucscmd);
int count = 0;
int timeout = 600;
while (count < timeout) {
if (isProfileAssociated(mgrvo.getId(), bvo.getDn())) {
break;
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new CloudRuntimeException(e);
}
count += 2;
}
if (count >= timeout) {
throw new CloudRuntimeException(String.format("associating profile[%s] to balde[%s] timeout after 600 seconds", pdn, bvo.getDn()));
}
s_logger.debug(String.format("successfully associated profile[%s] to blade[%s]", pdn, bvo.getDn()));
}
@Override
public ListResponse<ListUcsManagerResponse> listUcsManager(ListUcsManagerCmd cmd) {
SearchCriteriaService<UcsManagerVO, UcsManagerVO> serv = SearchCriteria2.create(UcsManagerVO.class);
serv.addAnd(serv.getEntity().getZoneId(), Op.EQ, cmd.getZoneId());
List<UcsManagerVO> vos = serv.list();
List<ListUcsManagerResponse> rsps = new ArrayList<ListUcsManagerResponse>(vos.size());
for (UcsManagerVO vo : vos) {
ListUcsManagerResponse rsp = new ListUcsManagerResponse();
rsp.setObjectName("ucsmanager");
rsp.setId(String.valueOf(vo.getId()));
rsp.setName(vo.getName());
rsp.setZoneId(String.valueOf(vo.getZoneId()));
rsps.add(rsp);
}
ListResponse<ListUcsManagerResponse> response = new ListResponse<ListUcsManagerResponse>();
response.setResponses(rsps);
return response;
}
@Override
public void setName(String name) {
// TODO Auto-generated method stub
}
@Override
public void setConfigParams(Map<String, Object> params) {
// TODO Auto-generated method stub
}
@Override
public Map<String, Object> getConfigParams() {
// TODO Auto-generated method stub
return null;
}
@Override
public int getRunLevel() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setRunLevel(int level) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,165 @@
package com.cloud.ucs.structure;
import java.util.ArrayList;
import java.util.List;
import com.cloud.utils.xmlobject.XmlObject;
import com.cloud.utils.xmlobject.XmlObjectParser;
public class ComputeBlade {
String adminPower;
String adminState;
String assignedToDn;
String association;
String availability;
String availableMemory;
String chassisId;
String dn;
String name;
String numOfAdaptors;
String numOfCores;
String numOfCoresEnabled;
String numOfCpus;
String numOfEthHostIfs;
String numOfFcHostIfs;
String numOfThreads;
String operPower;
String totalMemory;
String uuid;
public static List<ComputeBlade> fromXmString(String xmlstr) {
XmlObject root = XmlObjectParser.parseFromString(xmlstr);
List<XmlObject> lst = root.getAsList("configResolveClass.outConfigs.computeBlade");
List<ComputeBlade> blades = new ArrayList<ComputeBlade>();
if (lst == null) {
return blades;
}
for (XmlObject xo : lst) {
blades.add(fromXmlObject(xo));
}
return blades;
}
public static ComputeBlade fromXmlObject(XmlObject obj) {
ComputeBlade ret = new ComputeBlade();
return obj.evaluateObject(ret);
}
public String getAdminPower() {
return adminPower;
}
public void setAdminPower(String adminPower) {
this.adminPower = adminPower;
}
public String getAdminState() {
return adminState;
}
public void setAdminState(String adminState) {
this.adminState = adminState;
}
public String getAssignedToDn() {
return assignedToDn;
}
public void setAssignedToDn(String assignedToDn) {
this.assignedToDn = assignedToDn;
}
public String getAssociation() {
return association;
}
public void setAssociation(String association) {
this.association = association;
}
public String getAvailability() {
return availability;
}
public void setAvailability(String availability) {
this.availability = availability;
}
public String getAvailableMemory() {
return availableMemory;
}
public void setAvailableMemory(String availableMemory) {
this.availableMemory = availableMemory;
}
public String getChassisId() {
return chassisId;
}
public void setChassisId(String chassisId) {
this.chassisId = chassisId;
}
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumOfAdaptors() {
return numOfAdaptors;
}
public void setNumOfAdaptors(String numOfAdaptors) {
this.numOfAdaptors = numOfAdaptors;
}
public String getNumOfCores() {
return numOfCores;
}
public void setNumOfCores(String numOfCores) {
this.numOfCores = numOfCores;
}
public String getNumOfCoresEnabled() {
return numOfCoresEnabled;
}
public void setNumOfCoresEnabled(String numOfCoresEnabled) {
this.numOfCoresEnabled = numOfCoresEnabled;
}
public String getNumOfCpus() {
return numOfCpus;
}
public void setNumOfCpus(String numOfCpus) {
this.numOfCpus = numOfCpus;
}
public String getNumOfEthHostIfs() {
return numOfEthHostIfs;
}
public void setNumOfEthHostIfs(String numOfEthHostIfs) {
this.numOfEthHostIfs = numOfEthHostIfs;
}
public String getNumOfFcHostIfs() {
return numOfFcHostIfs;
}
public void setNumOfFcHostIfs(String numOfFcHostIfs) {
this.numOfFcHostIfs = numOfFcHostIfs;
}
public String getNumOfThreads() {
return numOfThreads;
}
public void setNumOfThreads(String numOfThreads) {
this.numOfThreads = numOfThreads;
}
public String getOperPower() {
return operPower;
}
public void setOperPower(String operPower) {
this.operPower = operPower;
}
public String getTotalMemory() {
return totalMemory;
}
public void setTotalMemory(String totalMemory) {
this.totalMemory = totalMemory;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public boolean isAssociated() {
return this.assignedToDn.equals("");
}
}

View File

@ -0,0 +1,37 @@
package com.cloud.ucs.structure;
import java.util.ArrayList;
import java.util.List;
import com.cloud.utils.xmlobject.XmlObject;
import com.cloud.utils.xmlobject.XmlObjectParser;
public class UcsProfile {
private String dn;
public static UcsProfile fromXmlObject(XmlObject xo) {
UcsProfile p = new UcsProfile();
return xo.evaluateObject(p);
}
public static List<UcsProfile> fromXmlString(String xmlstr) {
List<UcsProfile> ps = new ArrayList<UcsProfile>();
XmlObject xo = XmlObjectParser.parseFromString(xmlstr);
List<XmlObject> xos = xo.getAsList("outDns.dn");
if (xos != null) {
for (XmlObject x : xos) {
UcsProfile p = UcsProfile.fromXmlObject(x);
ps.add(p);
}
}
return ps;
}
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
}

5
plugins/pom.xml Normal file → Executable file
View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@ -15,8 +16,7 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudstack-plugins</artifactId>
<name>Apache CloudStack Plugin POM</name>
@ -43,6 +43,7 @@
<module>hypervisors/kvm</module>
<module>hypervisors/simulator</module>
<module>hypervisors/baremetal</module>
<module>hypervisors/ucs</module>
<module>network-elements/elastic-loadbalancer</module>
<module>network-elements/ovs</module>
<module>network-elements/nicira-nvp</module>

View File

@ -74,4 +74,8 @@ public class CloudRuntimeException extends RuntimeException {
public int getCSErrorCode() {
return this.csErrorCode;
}
public CloudRuntimeException(Throwable t) {
super(t);
}
}

View File

@ -0,0 +1,177 @@
package com.cloud.utils.xmlobject;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.cloud.utils.exception.CloudRuntimeException;
import edu.emory.mathcs.backport.java.util.Collections;
public class XmlObject {
private Map<String, Object> elements = new HashMap<String, Object>();
private String text;
private String tag;
XmlObject() {
}
public XmlObject(String tag) {
this.tag = tag;
}
public XmlObject putElement(String key, Object e) {
Object old = elements.get(key);
if (old == null) {
System.out.println(String.format("no %s, add new", key));
elements.put(key, e);
} else {
if (old instanceof List) {
System.out.println(String.format("already list %s, add", key));
((List)old).add(e);
} else {
System.out.println(String.format("not list list %s, add list", key));
List lst = new ArrayList();
lst.add(old);
lst.add(e);
elements.put(key, lst);
}
}
return this;
}
private Object recurGet(XmlObject obj, Iterator<String> it) {
String key = it.next();
Object e = obj.elements.get(key);
if (e == null) {
return null;
}
if (!it.hasNext()) {
return e;
} else {
if (!(e instanceof XmlObject)) {
throw new CloudRuntimeException(String.format("%s doesn't reference to a XmlObject", it.next()));
}
return recurGet((XmlObject) e, it);
}
}
public <T> T get(String elementStr) {
String[] strs = elementStr.split("\\.");
List<String> lst = new ArrayList<String>(strs.length);
Collections.addAll(lst, strs);
return (T)recurGet(this, lst.iterator());
}
public <T> List<T> getAsList(String elementStr) {
Object e = get(elementStr);
if (e instanceof List) {
return (List<T>)e;
}
List lst = new ArrayList(1);
lst.add(e);
return lst;
}
public String getText() {
return text;
}
public XmlObject setText(String text) {
this.text = text;
return this;
}
public String getTag() {
return tag;
}
public XmlObject setTag(String tag) {
this.tag = tag;
return this;
}
public String dump() {
StringBuilder sb = new StringBuilder();
sb.append("<").append(tag);
List<XmlObject> children = new ArrayList<XmlObject>();
for (Map.Entry<String, Object> e : elements.entrySet()) {
String key = e.getKey();
Object val = e.getValue();
if (val instanceof String) {
sb.append(String.format(" %s=\"%s\"", key, val.toString()));
} else if (val instanceof XmlObject) {
children.add((XmlObject) val);
} else if (val instanceof List) {
children.addAll((Collection<? extends XmlObject>) val);
} else {
throw new CloudRuntimeException(String.format("unsupported element type[tag:%s, class: %s], only allowed type of [String, List<XmlObject>, Object]", key, val.getClass().getName()));
}
}
if (!children.isEmpty() && text != null) {
throw new CloudRuntimeException(String.format("element %s cannot have both text[%s] and child elements", tag, text));
}
if (!children.isEmpty()) {
sb.append(">");
for (XmlObject x : children) {
sb.append(x.dump());
}
sb.append(String.format("</%s>", tag));
} else {
if (text != null) {
sb.append(">");
sb.append(text);
sb.append(String.format("</%s>", tag));
} else {
sb.append(" />");
}
}
return sb.toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("<" + tag);
for (Map.Entry<String, Object> e : elements.entrySet()) {
String key = e.getKey();
Object value = e.getValue();
if (!(value instanceof String)) {
continue;
}
sb.append(String.format(" %s=\"%s\"", key, value.toString()));
}
if (text == null || "".equals(text.trim())) {
sb.append(" />");
} else {
sb.append(">").append(text).append(String.format("</ %s>", tag));
}
return sb.toString();
}
public <T> T evaluateObject(T obj) {
Class<?> clazz = obj.getClass();
try {
do {
Field[] fs = clazz.getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
Object value = get(f.getName());
f.set(obj, value);
}
clazz = clazz.getSuperclass();
} while (clazz != null && clazz != Object.class);
return obj;
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
}
}

View File

@ -0,0 +1,107 @@
package com.cloud.utils.xmlobject;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Stack;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.cloud.utils.exception.CloudRuntimeException;
public class XmlObjectParser {
final private InputStream is;
private class XmlHandler extends DefaultHandler {
private Stack<XmlObject> stack;
private String currentValue;
private XmlObject root;
XmlHandler() {
stack = new Stack<XmlObject>();
}
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
//System.out.println(String.format("startElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName));
currentValue = null;
XmlObject obj = new XmlObject();
for (int i=0; i<atts.getLength(); i++) {
obj.putElement(atts.getQName(i), atts.getValue(i));
}
obj.setTag(qName);
if (!stack.isEmpty()) {
XmlObject parent = stack.peek();
parent.putElement(qName, obj);
}
stack.push(obj);
}
@Override
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
XmlObject currObj = stack.pop();
if (currentValue != null) {
currObj.setText(currentValue);
}
if (stack.isEmpty()) {
root = currObj;
}
//System.out.println(String.format("endElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName));
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
StringBuilder str = new StringBuilder();
str.append(ch, start, length);
currentValue = str.toString();
//System.out.println(String.format("characters: %s", str.toString()));
}
XmlObject getRoot() {
return root;
}
}
private XmlObjectParser(InputStream is) {
super();
this.is = is;
}
public static XmlObject parseFromFile(String filePath) {
FileInputStream fs;
try {
fs = new FileInputStream(new File(filePath));
XmlObjectParser p = new XmlObjectParser(fs);
return p.parse();
} catch (FileNotFoundException e) {
throw new CloudRuntimeException(e);
}
}
public static XmlObject parseFromString(String xmlString) {
InputStream stream = new ByteArrayInputStream(xmlString.getBytes());
XmlObjectParser p = new XmlObjectParser(stream);
return p.parse();
}
private XmlObject parse() {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = spfactory.newSAXParser();
XmlHandler handler = new XmlHandler();
saxParser.parse(is, handler);
return handler.getRoot();
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
}
}

View File

@ -0,0 +1,29 @@
package com.cloud.utils.xmlobject;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
public class TestXmlObject {
void p(String str) {
System.out.println(str);
}
@Test
public void test() {
XmlObject xo = XmlObjectParser.parseFromFile("z:/components.xml.in");
p(xo.getTag());
p((String) xo.get("system-integrity-checker.checker").toString());
List<XmlObject> lst = xo.get("management-server.adapters");
for (XmlObject x : lst) {
List<XmlObject> lst1 = x.getAsList("adapter");
for (XmlObject y : lst1) {
p(y.toString());
}
}
}
}

View File

@ -0,0 +1,37 @@
package com.cloud.utils.xmlobject;
import static org.junit.Assert.*;
import org.junit.Test;
public class TestXmlObject2 {
void p(String str) {
System.out.println(str);
}
XmlObject xo(String name) {
return new XmlObject(name);
}
@Test
public void test() {
XmlObject root = new XmlObject("test");
root.putElement("key1", "value1").putElement("key2", "value2");
p(root.dump());
XmlObject c1 = new XmlObject("child1");
XmlObject c2 = new XmlObject("child2");
c2.putElement("ckey1", "value1");
c1.putElement(c2.getTag(), c2);
root.putElement(c1.getTag(), c1);
p(root.dump());
root = xo("test2").putElement("key1", "value1").putElement("child1", xo("child1").setText("yyy"))
.putElement("child1", xo("child1")
.putElement("child2", xo("child2")
.putElement("child3", xo("child3").putElement("key3", "value3").setText("xxxxx"))));
p(root.dump());
}
}