CLOUDSTACK-6278

Baremetal Advanced Networking support
This commit is contained in:
Frank Zhang 2014-09-17 12:39:21 -07:00
parent d315a5e2c9
commit 8b89494a35
13 changed files with 187 additions and 97 deletions

View File

@ -28,7 +28,13 @@
>
<bean id="BaremetalManager" class="com.cloud.baremetal.manager.BaremetalManagerImpl" />
<bean id="BaremetalVlanManager" class="com.cloud.baremetal.manager.BaremetalVlanManagerImpl" />
<bean id="BaremetalVlanManager" class="com.cloud.baremetal.manager.BaremetalVlanManagerImpl" >
<property name="backends">
<map>
<entry key="Force10" value-ref="Force10BaremetalSwitchBackend"/>
</map>
</property>
</bean>
<bean id="Force10BaremetalSwitchBackend" class="com.cloud.baremetal.networkservice.Force10BaremetalSwitchBackend" />
<bean id="BaremetalKickStartPxeService"

View File

@ -18,6 +18,7 @@
package com.cloud.baremetal.manager;
import com.cloud.baremetal.networkservice.BaremetalRctResponse;
import com.cloud.baremetal.networkservice.BaremetalSwitchBackend;
import com.cloud.deploy.DeployDestination;
import com.cloud.network.Network;
import com.cloud.utils.component.Manager;
@ -36,4 +37,6 @@ public interface BaremetalVlanManager extends Manager, PluggableService {
void prepareVlan(Network nw, DeployDestination destHost);
void releaseVlan(Network nw, VirtualMachineProfile vm);
void registerSwitchBackend(BaremetalSwitchBackend backend);
}

View File

@ -47,7 +47,6 @@ import javax.inject.Inject;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -69,13 +68,17 @@ public class BaremetalVlanManagerImpl extends ManagerBase implements BaremetalVl
@Inject
private AccountManager acntMgr;
private Map<String, BaremetalSwitchBackend> backends = new HashMap<>();
private Map<String, BaremetalSwitchBackend> backends;
private class RackPair {
BaremetalRct.Rack rack;
BaremetalRct.HostEntry host;
}
public void setBackends(Map<String, BaremetalSwitchBackend> 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) {

View File

@ -391,7 +391,7 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource
VMInstanceVO vm = vms.get(0);
SecurityGroupHttpClient client = new SecurityGroupHttpClient();
HashMap<String, Pair<Long, Long>> 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());

View File

@ -219,19 +219,19 @@ public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase imple
}
List<String> tuple = parseKickstartUrl(profile);
Pair<Boolean, String> 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<Boolean, String> 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()));
}

View File

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

View File

@ -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<HttpStatus> 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<String> 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<XmlObject> ports = xml.getAsList("tagged.tengigabitethernet");
ports.addAll(xml.<XmlObject>getAsList("tagged.gigabitethernet"));
ports.addAll(xml.<XmlObject>getAsList("tagged.fortyGigE"));
List<XmlObject> ports = xml.getAsList("untagged.tengigabitethernet");
ports.addAll(xml.<XmlObject>getAsList("untagged.gigabitethernet"));
ports.addAll(xml.<XmlObject>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<String> 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<XmlObject> ports = xml.getAsList("tagged.tengigabitethernet");
ports.addAll(xml.<XmlObject>getAsList("tagged.gigabitethernet"));
ports.addAll(xml.<XmlObject>getAsList("tagged.fortyGigE"));
List<XmlObject> ports = xml.getAsList("untagged.tengigabitethernet");
ports.addAll(xml.<XmlObject>getAsList("untagged.gigabitethernet"));
ports.addAll(xml.<XmlObject>getAsList("untagged.fortyGigE"));
List<XmlObject> 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;
}

View File

@ -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

View File

@ -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 <<EOF
DEFAULT default
PROMPT 1
@ -70,6 +35,24 @@ APPEND ramdisk_size=66000 initrd=$initrd_path ks=$ks_file
EOF
tmpt_dir=$TFTP_ROOT/$tmpt_uuid
if [ -d $tmpt_dir ]; then
success
fi
mkdir -p $tmpt_dir
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
success

View File

@ -415,7 +415,7 @@ dhcp-option=vendor:MSFT,2,1i
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
@ -472,10 +472,10 @@ dhcp-option=vendor:MSFT,2,1i
# Enable dnsmasq's built-in TFTP server
#enable-tftp
enable-tftp
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
tftp-root=/opt/tftpboot
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.

View File

@ -27,10 +27,12 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import com.cloud.utils.exception.CloudRuntimeException;
public class XmlObject {
private final Logger logger = Logger.getLogger(XmlObject.class.getName());
private final Map<String, Object> elements = new HashMap<String, Object>();
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<T>)e;
}
List lst = new ArrayList(1);
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()) {

View File

@ -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() {

View File

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