From 8b89494a350979162d986edb5125970222895324 Mon Sep 17 00:00:00 2001 From: Frank Zhang Date: Wed, 17 Sep 2014 12:39:21 -0700 Subject: [PATCH] CLOUDSTACK-6278 Baremetal Advanced Networking support --- .../core/spring-baremetal-core-context.xml | 8 +- .../manager/BaremetalVlanManager.java | 3 + .../manager/BaremetalVlanManagerImpl.java | 12 +- .../networkservice/BareMetalResourceBase.java | 2 +- .../BaremetalKickStartServiceImpl.java | 14 +-- .../networkservice/BaremetalPxeElement.java | 5 +- .../Force10BaremetalSwitchBackend.java | 118 +++++++++++++----- scripts/network/ping/baremetal_snat.sh | 21 +++- scripts/network/ping/prepare_pxe.sh | 53 +++----- .../debian/config/etc/dnsmasq.conf.tmpl | 6 +- .../com/cloud/utils/xmlobject/XmlObject.java | 15 ++- .../utils/xmlobject/XmlObjectParser.java | 22 ++-- .../cloud/utils/xmlobject/TestXmlObject.java | 5 + 13 files changed, 187 insertions(+), 97 deletions(-) mode change 100644 => 100755 plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java mode change 100644 => 100755 utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java mode change 100644 => 100755 utils/test/com/cloud/utils/xmlobject/TestXmlObject.java diff --git a/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml b/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml index ae28efad6db..a153e7fb0c6 100755 --- a/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml +++ b/plugins/hypervisors/baremetal/resources/META-INF/cloudstack/core/spring-baremetal-core-context.xml @@ -28,7 +28,13 @@ > - + + + + + + + backends = new HashMap<>(); + private Map backends; private class RackPair { BaremetalRct.Rack rack; BaremetalRct.HostEntry host; } + public void setBackends(Map backends) { + this.backends = backends; + } + @Override public BaremetalRctResponse addRct(AddBaremetalRctCmd cmd) { try { @@ -161,6 +164,11 @@ public class BaremetalVlanManagerImpl extends ManagerBase implements BaremetalVl backend.removePortFromVlan(struct); } + @Override + public void registerSwitchBackend(BaremetalSwitchBackend backend) { + backends.put(backend.getSwitchBackendType(), backend); + } + private BaremetalSwitchBackend getSwitchBackend(String type) { BaremetalSwitchBackend backend = backends.get(type); if (backend == null) { diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java index fedfb8bd9f6..49a935a4344 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java @@ -391,7 +391,7 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource VMInstanceVO vm = vms.get(0); SecurityGroupHttpClient client = new SecurityGroupHttpClient(); HashMap> nwGrpStates = client.sync(vm.getInstanceName(), vm.getId(), vm.getPrivateIpAddress()); - return new PingRoutingWithNwGroupsCommand(getType(), id, getHostVmStateReport(), nwGrpStates); + return new PingRoutingWithNwGroupsCommand(getType(), id, null, nwGrpStates); } } else { return new PingRoutingCommand(getType(), id, getHostVmStateReport()); diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java index bd363f698aa..2e236c252ac 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java @@ -219,19 +219,19 @@ public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase imple } List tuple = parseKickstartUrl(profile); - Pair ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, - String.format("/usr/bin/prepare_pxe.sh %s %s %s %s %s", tuple.get(1), tuple.get(2), profile.getTemplate().getUuid(), - String.format("01-%s", nic.getMacAddress().replaceAll(":", "-")), tuple.get(0)) - ); + String cmd = String.format("/usr/bin/prepare_pxe.sh %s %s %s %s %s %s", tuple.get(1), tuple.get(2), profile.getTemplate().getUuid(), + String.format("01-%s", nic.getMacAddress().replaceAll(":", "-")).toLowerCase(), tuple.get(0), nic.getMacAddress().toLowerCase()); + s_logger.debug(String.format("prepare pxe on virtual router[ip:%s], cmd: %s", mgmtNic.getIp4Address(), cmd)); + Pair ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, cmd); if (!ret.first()) { throw new CloudRuntimeException(String.format("failed preparing PXE in virtual router[id:%s], because %s", vr.getId(), ret.second())); } //String internalServerIp = _configDao.getValue(Config.BaremetalInternalStorageServer.key()); String internalServerIp = "10.223.110.231"; - ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, - String.format("/usr/bin/baremetal_snat.sh %s %s", mgmtNic.getIp4Address(), internalServerIp) - ); + cmd = String.format("/usr/bin/baremetal_snat.sh %s %s %s", mgmtNic.getIp4Address(), internalServerIp, mgmtNic.getGateway()); + s_logger.debug(String.format("prepare SNAT on virtual router[ip:%s], cmd: %s", mgmtNic.getIp4Address(), cmd)); + ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, cmd); if (!ret.first()) { throw new CloudRuntimeException(String.format("failed preparing PXE in virtual router[id:%s], because %s", vr.getId(), ret.second())); } diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java index 5d20d3130f5..10b81c2b3d7 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java @@ -21,6 +21,7 @@ package com.cloud.baremetal.networkservice; import com.cloud.baremetal.database.BaremetalPxeVO; import com.cloud.baremetal.manager.BaremetalVlanManager; import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeployDestination; @@ -139,11 +140,9 @@ public class BaremetalPxeElement extends AdapterBase implements NetworkElement { } } - /* if (dest.getDataCenter().getNetworkType() == DataCenter.NetworkType.Advanced){ prepareVlan(network, dest); } - */ return true; } @@ -159,12 +158,10 @@ public class BaremetalPxeElement extends AdapterBase implements NetworkElement { return false; } - /* DataCenterVO dc = zoneDao.findById(vm.getVirtualMachine().getDataCenterId()); if (dc.getNetworkType() == DataCenter.NetworkType.Advanced) { releaseVlan(network, vm); } - */ return true; } diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java old mode 100644 new mode 100755 index 9009b02d3fb..ceaf25cb57b --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/Force10BaremetalSwitchBackend.java @@ -32,11 +32,16 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -46,17 +51,38 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { private Logger logger = Logger.getLogger(Force10BaremetalSwitchBackend.class); public static final String TYPE = "Force10"; - RestTemplate rest = new RestTemplate(); + private static List successHttpStatusCode = new ArrayList<>(); + { + successHttpStatusCode.add(HttpStatus.OK); + successHttpStatusCode.add(HttpStatus.ACCEPTED); + successHttpStatusCode.add(HttpStatus.CREATED); + successHttpStatusCode.add(HttpStatus.NO_CONTENT); + successHttpStatusCode.add(HttpStatus.PARTIAL_CONTENT); + successHttpStatusCode.add(HttpStatus.RESET_CONTENT); + successHttpStatusCode.add(HttpStatus.ALREADY_REPORTED); + } - private String buildLink(String switchIp, Integer vlan) { + RestTemplate rest = new RestTemplate(); + { + // fake error handler, we handle error in business logic code + rest.setErrorHandler(new ResponseErrorHandler() { + @Override + public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { + return false; + } + + @Override + public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { + } + }); + } + + private String buildLink(String switchIp, String path) { UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); builder.scheme("http"); builder.host(switchIp); builder.port(8008); - builder.path("/api/running/ftos/interface/vlan"); - if (vlan != null) { - builder.path(vlan.toString()); - } + builder.path(path); return builder.build().toUriString(); } @@ -67,29 +93,36 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { @Override public void prepareVlan(BaremetalVlanStruct struct) { - String link = buildLink(struct.getSwitchIp(), struct.getVlan()); + String link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/vlan/%s", struct.getVlan())); HttpHeaders headers = createBasicAuthenticationHeader(struct); HttpEntity request = new HttpEntity<>(headers); ResponseEntity rsp = rest.exchange(link, HttpMethod.GET, request, String.class); + logger.debug(String.format("http get: %s", link)); if (rsp.getStatusCode() == HttpStatus.NOT_FOUND) { PortInfo port = new PortInfo(struct); - XmlObject xml = new XmlObject("vlan").putElement("vlan-id", String.valueOf(struct.getVlan())).putElement("tagged", - new XmlObject(port.interfaceType).putElement("name", port.port) - ).putElement("shutdown", "false"); - request = new HttpEntity<>(xml.toString(), headers); - link = buildLink(struct.getSwitchIp(), null); - rsp = rest.exchange(link, HttpMethod.GET, request, String.class); - if (rsp.getStatusCode() != HttpStatus.OK) { + XmlObject xml = new XmlObject("vlan").putElement("vlan-id", + new XmlObject("vlan-id").setText(String.valueOf(struct.getVlan()))).putElement("untagged", + new XmlObject("untagged").putElement(port.interfaceType, new XmlObject(port.interfaceType) + .putElement("name", new XmlObject("name").setText(port.port))) + ).putElement("shutdown", new XmlObject("shutdown").setText("false")); + request = new HttpEntity<>(xml.dump(), headers); + link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/")); + logger.debug(String.format("http get: %s, body: %s", link, request)); + rsp = rest.exchange(link, HttpMethod.POST, request, String.class); + if (!successHttpStatusCode.contains(rsp.getStatusCode())) { throw new CloudRuntimeException(String.format("unable to create vlan[%s] on force10 switch[ip:%s]. HTTP status code:%s, body dump:%s", - struct.getVlan(), rsp.getStatusCode(), struct.getSwitchIp(), rsp.getBody())); + struct.getVlan(), struct.getSwitchIp(),rsp.getStatusCode(), rsp.getBody())); + } else { + logger.debug(String.format("successfully programmed vlan[%s] on Force10[ip:%s, port:%s]. http response[status code:%s, body:%s]", + struct.getVlan(), struct.getSwitchIp(), struct.getPort(), rsp.getStatusCode(), rsp.getBody())); } - } else if (rsp.getStatusCode() == HttpStatus.OK) { + } else if (successHttpStatusCode.contains(rsp.getStatusCode())) { PortInfo port = new PortInfo(struct); XmlObject xml = XmlObjectParser.parseFromString((String)rsp.getBody()); - List ports = xml.getAsList("tagged.tengigabitethernet"); - ports.addAll(xml.getAsList("tagged.gigabitethernet")); - ports.addAll(xml.getAsList("tagged.fortyGigE")); + List ports = xml.getAsList("untagged.tengigabitethernet"); + ports.addAll(xml.getAsList("untagged.gigabitethernet")); + ports.addAll(xml.getAsList("untagged.fortyGigE")); for (XmlObject pxml : ports) { XmlObject name = pxml.get("name"); if (port.port.equals(name.getText())) { @@ -98,14 +131,26 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { } } - XmlObject tag = xml.get("tagged"); - tag.putElement(port.interfaceType, new XmlObject("name").setText(port.port)); - request = new HttpEntity<>(xml.toString(), headers); - link = buildLink(struct.getSwitchIp(), struct.getVlan()); + xml.removeElement("mtu"); + xml.setText(null); + XmlObject tag = xml.get("untagged"); + if (tag == null) { + tag = new XmlObject("untagged"); + xml.putElement("untagged", tag); + } + + tag.putElement(port.interfaceType, new XmlObject(port.interfaceType) + .putElement("name", new XmlObject("name").setText(port.port))); + request = new HttpEntity<>(xml.dump(), headers); + link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/vlan/%s", struct.getVlan())); + logger.debug(String.format("http get: %s, body: %s", link, request)); rsp = rest.exchange(link, HttpMethod.PUT, request, String.class); - if (rsp.getStatusCode() != HttpStatus.NO_CONTENT) { + if (!successHttpStatusCode.contains(rsp.getStatusCode())) { throw new CloudRuntimeException(String.format("failed to program vlan[%s] for port[%s] on force10[ip:%s]. http status:%s, body dump:%s", struct.getVlan(), struct.getPort(), struct.getSwitchIp(), rsp.getStatusCode(), rsp.getBody())); + } else { + logger.debug(String.format("successfully join port[%s] into vlan[%s] on Force10[ip:%s]. http response[status code:%s, body:%s]", + struct.getPort(), struct.getVlan(), struct.getSwitchIp(), rsp.getStatusCode(), rsp.getBody())); } } else { throw new CloudRuntimeException(String.format("force10[ip:%s] returns unexpected error[%s] when http getting %s, body dump:%s", @@ -115,18 +160,19 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { @Override public void removePortFromVlan(BaremetalVlanStruct struct) { - String link = buildLink(struct.getSwitchIp(), struct.getVlan()); + String link = buildLink(struct.getSwitchIp(), String.format("/api/running/ftos/interface/vlan/%s", struct.getVlan())); HttpHeaders headers = createBasicAuthenticationHeader(struct); HttpEntity request = new HttpEntity<>(headers); + logger.debug(String.format("http get: %s, body: %s", link, request)); ResponseEntity rsp = rest.exchange(link, HttpMethod.GET, request, String.class); if (rsp.getStatusCode() == HttpStatus.NOT_FOUND) { logger.debug(String.format("vlan[%s] has been deleted on force10[ip:%s], no need to remove the port[%s] anymore", struct.getVlan(), struct.getSwitchIp(), struct.getPort())); } else if (rsp.getStatusCode() == HttpStatus.OK) { PortInfo port = new PortInfo(struct); XmlObject xml = XmlObjectParser.parseFromString((String)rsp.getBody()); - List ports = xml.getAsList("tagged.tengigabitethernet"); - ports.addAll(xml.getAsList("tagged.gigabitethernet")); - ports.addAll(xml.getAsList("tagged.fortyGigE")); + List ports = xml.getAsList("untagged.tengigabitethernet"); + ports.addAll(xml.getAsList("untagged.gigabitethernet")); + ports.addAll(xml.getAsList("untagged.fortyGigE")); List newPorts = new ArrayList<>(); boolean needRemove = false; for (XmlObject pxml : ports) { @@ -143,11 +189,19 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { return; } - xml.putElement("tagged", newPorts); + xml.setText(null); + xml.removeElement("mtu"); + XmlObject tagged = xml.get("untagged"); + tagged.removeAllChildren(); + for (XmlObject p : newPorts) { + tagged.putElement(p.getTag(), p); + } - request = new HttpEntity<>(xml.toString(), headers); + + request = new HttpEntity<>(xml.dump(), headers); + logger.debug(String.format("http get: %s, body: %s", link, request)); rsp = rest.exchange(link, HttpMethod.PUT, request, String.class); - if (rsp.getStatusCode() != HttpStatus.NO_CONTENT) { + if (!successHttpStatusCode.contains(rsp.getStatusCode())) { throw new CloudRuntimeException(String.format("failed to program vlan[%s] for port[%s] on force10[ip:%s]. http status:%s, body dump:%s", struct.getVlan(), struct.getPort(), struct.getSwitchIp(), rsp.getStatusCode(), rsp.getBody())); } else { @@ -166,6 +220,8 @@ public class Force10BaremetalSwitchBackend implements BaremetalSwitchBackend { String base64Creds = new String(base64CredsBytes); HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", "Basic " + base64Creds); + headers.setAccept(Arrays.asList(MediaType.ALL)); + headers.setContentType(MediaType.valueOf("application/vnd.yang.data+xml")); return headers; } diff --git a/scripts/network/ping/baremetal_snat.sh b/scripts/network/ping/baremetal_snat.sh index f35a16f8e96..22e56692d7d 100755 --- a/scripts/network/ping/baremetal_snat.sh +++ b/scripts/network/ping/baremetal_snat.sh @@ -21,11 +21,12 @@ set +u mgmt_nic_ip=$1 internal_server_ip=$2 +gateway_ip=$3 ip route | grep "$internal_server_ip" > /dev/null if [ $? -ne 0 ]; then - ip route add $internal_server_ip via $mgmt_nic_ip + ip route add $internal_server_ip via $gateway_ip fi iptables-save | grep -- "-A POSTROUTING -d $internal_server_ip" > /dev/null @@ -33,3 +34,21 @@ iptables-save | grep -- "-A POSTROUTING -d $internal_server_ip" > /dev/null if [ $? -ne 0 ]; then iptables -t nat -A POSTROUTING -d $internal_server_ip -j SNAT --to-source $mgmt_nic_ip fi + + +iptables-save | grep -- "-A INPUT -i eth0 -p udp -m udp --dport 69 -j ACCEPT" > /dev/null +if [ $? -ne 0 ]; then + iptables -I INPUT -i eth0 -p udp -m udp --dport 69 -j ACCEPT +fi + +iptables-save | grep -- "-A FORWARD -i eth1 -o eth0 -j ACCEPT" > /dev/null +if [ $? -ne 0 ]; then + iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT +fi + +rule="-A FORWARD -d $internal_server_ip/32 -i eth0 -o eth1 -j ACCEPT" +iptables-save | grep -- "$rule" > /dev/null +if [ $? -ne 0 ]; then + iptables -I FORWARD -d $internal_server_ip/32 -i eth0 -o eth1 -j ACCEPT +fi + diff --git a/scripts/network/ping/prepare_pxe.sh b/scripts/network/ping/prepare_pxe.sh index 2f22b25099d..2970b4ae0a5 100755 --- a/scripts/network/ping/prepare_pxe.sh +++ b/scripts/network/ping/prepare_pxe.sh @@ -1,21 +1,4 @@ #!/bin/sh - -#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://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. set +u err_exit() { @@ -38,27 +21,9 @@ tmpt_uuid=$3 pxe_cfg_filename=$4 ks_file=$5 -tmpt_dir=$TFTP_ROOT/$tmpt_uuid -if [ -d $tmpt_dir ]; then - success -fi - -mkdir -p $tmpt_dir - kernel_path=$tmpt_uuid/$kernel_file_name initrd_path=$tmpt_uuid/$initrd_file_name -mnt_path=/tmp/$(uuid) - -mkdir -p $mnt_path -mount `dirname $kernel_nfs_path` $mnt_path -cp -f $mnt_path/$kernel_file_name $tmpt_dir/$kernel_file_name -umount $mnt_path - -mount `dirname $initrd_nfs_path` $mnt_path -cp -f $mnt_path/$initrd_file_name $tmpt_dir/$initrd_file_name -umount $mnt_path - cat > $PXELINUX_CFG_DIR/$pxe_cfg_filename < elements = new HashMap(); private String text; private String tag; @@ -38,6 +40,10 @@ public class XmlObject { XmlObject() { } + public void removeAllChildren() { + elements.clear(); + } + public XmlObject(String tag) { this.tag = tag; } @@ -99,8 +105,12 @@ public class XmlObject { if (e instanceof List) { return (List)e; } + List lst = new ArrayList(1); - lst.add(e); + if (e != null) { + lst.add(e); + } + return lst; } @@ -142,7 +152,8 @@ public class XmlObject { } if (!children.isEmpty() && text != null) { - throw new CloudRuntimeException(String.format("element %s cannot have both text[%s] and child elements", tag, text)); + logger.info(String.format("element %s cannot have both text[%s] and child elements, set text to null", tag, text)); + text = null; } if (!children.isEmpty()) { diff --git a/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java b/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java old mode 100644 new mode 100755 index 321af0ce9c8..f0e8ce31261 --- a/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java +++ b/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java @@ -19,6 +19,13 @@ package com.cloud.utils.xmlobject; +import com.cloud.utils.exception.CloudRuntimeException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -26,15 +33,6 @@ 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; @@ -109,7 +107,11 @@ public class XmlObjectParser { public static XmlObject parseFromString(String xmlString) { InputStream stream = new ByteArrayInputStream(xmlString.getBytes()); XmlObjectParser p = new XmlObjectParser(stream); - return p.parse(); + XmlObject obj = p.parse(); + if (obj.getText() != null && obj.getText().replaceAll("\\n", "").replaceAll("\\r", "").replaceAll(" ", "").isEmpty()) { + obj.setText(null); + } + return obj; } private XmlObject parse() { diff --git a/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java b/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java old mode 100644 new mode 100755 index cbd24b029c4..faaf9803c14 --- a/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java +++ b/utils/test/com/cloud/utils/xmlobject/TestXmlObject.java @@ -43,6 +43,11 @@ public class TestXmlObject { } } */ + + XmlObject xml = new XmlObject("vlan").putElement("vlan-id", String.valueOf(19)).putElement("tagged", + new XmlObject("teng").putElement("name", "0/0") + ).putElement("shutdown", "false"); + System.out.println(xml.toString()); } }