2 * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.genius.itm.confighelpers;
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import org.apache.commons.net.util.SubnetUtils;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.genius.itm.globals.ITMConstants;
22 import org.opendaylight.genius.itm.impl.ItmUtils;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelMonitoringTypeBase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.tunnel.optional.params.TunnelOptions;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.ExternalTunnelList;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.tunnel.end.points.TzMembership;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnelKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.DeviceVteps;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 public class ItmExternalTunnelAddWorker {
51 private static final Logger LOG = LoggerFactory.getLogger(ItmExternalTunnelAddWorker.class);
53 private static Boolean monitorEnabled;
54 private static Integer monitorInterval;
55 private static Class<? extends TunnelMonitoringTypeBase> monitorProtocol;
57 private static final FutureCallback<Void> DEFAULT_CALLBACK =
58 new FutureCallback<Void>() {
60 public void onSuccess(Void result) {
61 LOG.debug("Success in Datastore operation");
65 public void onFailure(Throwable error) {
66 LOG.error("Error in Datastore operation", error);
71 public static List<ListenableFuture<Void>> buildTunnelsToExternalEndPoint(DataBroker dataBroker,
72 IdManagerService idManagerService,
73 List<DPNTEPsInfo> cfgDpnList,
75 Class<? extends TunnelTypeBase> tunType,
76 ItmConfig itmConfig) {
77 if (null != cfgDpnList) {
78 WriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
79 for (DPNTEPsInfo teps : cfgDpnList) {
80 // CHECK -- Assumption -- Only one End Point / Dpn for GRE/Vxlan Tunnels
81 TunnelEndPoints firstEndPt = teps.getTunnelEndPoints().get(0);
82 String interfaceName = firstEndPt.getInterfaceName();
83 String tunTypeStr = tunType.getName();
84 String trunkInterfaceName = ItmUtils.getTrunkInterfaceName(idManagerService, interfaceName,
85 new String(firstEndPt.getIpAddress().getValue()),
86 new String(extIp.getValue()), tunTypeStr);
87 char[] subnetMaskArray = firstEndPt.getSubnetMask().getValue();
88 boolean useOfTunnel = ItmUtils.falseIfNull(firstEndPt.isOptionOfTunnel());
89 List<TunnelOptions> tunOptions = ItmUtils.buildTunnelOptions(firstEndPt, itmConfig);
90 String subnetMaskStr = String.valueOf(subnetMaskArray);
91 SubnetUtils utils = new SubnetUtils(subnetMaskStr);
92 String dcGwyIpStr = String.valueOf(extIp.getValue());
93 IpAddress gatewayIpObj = new IpAddress("0.0.0.0".toCharArray());
94 IpAddress gwyIpAddress =
95 utils.getInfo().isInRange(dcGwyIpStr) ? gatewayIpObj : firstEndPt.getGwIpAddress();
96 LOG.debug(" Creating Trunk Interface with parameters trunk I/f Name - {}, parent I/f name - {},"
97 + " source IP - {}, DC Gateway IP - {} gateway IP - {}", trunkInterfaceName, interfaceName,
98 firstEndPt.getIpAddress(), extIp, gwyIpAddress);
99 Interface iface = ItmUtils.buildTunnelInterface(teps.getDPNID(), trunkInterfaceName,
100 String.format("%s %s", ItmUtils.convertTunnelTypetoString(tunType), "Trunk Interface"), true,
101 tunType, firstEndPt.getIpAddress(), extIp, gwyIpAddress, firstEndPt.getVLANID(), false, false,
102 ITMConstants.DEFAULT_MONITOR_PROTOCOL, null, useOfTunnel, tunOptions);
104 LOG.debug(" Trunk Interface builder - {} ", iface);
105 InstanceIdentifier<Interface> trunkIdentifier = ItmUtils.buildId(trunkInterfaceName);
106 LOG.debug(" Trunk Interface Identifier - {} ", trunkIdentifier);
107 LOG.trace(" Writing Trunk Interface to Config DS {}, {} ", trunkIdentifier, iface);
108 transaction.merge(LogicalDatastoreType.CONFIGURATION, trunkIdentifier, iface, true);
109 // update external_tunnel_list ds
110 InstanceIdentifier<ExternalTunnel> path = InstanceIdentifier.create(ExternalTunnelList.class)
111 .child(ExternalTunnel.class, new ExternalTunnelKey(extIp.toString(),
112 teps.getDPNID().toString(), tunType));
113 ExternalTunnel tnl = ItmUtils.buildExternalTunnel(teps.getDPNID().toString(), extIp.toString(),
114 tunType, trunkInterfaceName);
115 transaction.merge(LogicalDatastoreType.CONFIGURATION, path, tnl, true);
117 return Collections.singletonList(transaction.submit());
119 return Collections.emptyList();
122 public static List<ListenableFuture<Void>> buildTunnelsFromDpnToExternalEndPoint(DataBroker dataBroker,
123 IdManagerService idManagerService,
124 List<BigInteger> dpnId,
126 Class<? extends TunnelTypeBase>
128 ItmConfig itmConfig) {
129 List<DPNTEPsInfo> cfgDpnList = dpnId == null ? ItmUtils.getTunnelMeshInfo(dataBroker)
130 : ItmUtils.getDpnTepListFromDpnId(dataBroker, dpnId);
131 return buildTunnelsToExternalEndPoint(dataBroker, idManagerService, cfgDpnList, extIp, tunType, itmConfig);
134 public static List<ListenableFuture<Void>> buildHwVtepsTunnels(DataBroker dataBroker,
135 IdManagerService idManagerService,
136 List<DPNTEPsInfo> cfgdDpnList,
137 List<HwVtep> cfgdHwVteps) {
138 List<ListenableFuture<Void>> futures = new ArrayList<>();
139 WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
140 monitorInterval = ITMConstants.BFD_DEFAULT_MONITOR_INTERVAL;
141 monitorProtocol = ITMConstants.DEFAULT_MONITOR_PROTOCOL;
142 monitorEnabled = ItmUtils.readMonitoringStateFromCache(dataBroker);
143 if (null != cfgdDpnList && !cfgdDpnList.isEmpty()) {
144 LOG.trace("calling tunnels from css {}",cfgdDpnList);
145 tunnelsFromCSS(cfgdDpnList, idManagerService , futures, writeTransaction , dataBroker);
147 if (null != cfgdHwVteps && !cfgdHwVteps.isEmpty()) {
148 LOG.trace("calling tunnels from hwTep {}",cfgdHwVteps);
149 tunnelsFromhWVtep(cfgdHwVteps, idManagerService, futures, writeTransaction, dataBroker);
152 if (cfgdDpnList != null && !cfgdDpnList.isEmpty() || cfgdHwVteps != null && !cfgdHwVteps.isEmpty()) {
153 futures.add(writeTransaction.submit());
158 private static void tunnelsFromCSS(List<DPNTEPsInfo> cfgdDpnList, IdManagerService idManagerService,
159 List<ListenableFuture<Void>> futures, WriteTransaction transaction,
160 DataBroker dataBroker) {
161 Boolean monitorEnabled = ItmUtils.readMonitoringStateFromCache(dataBroker);
162 Class<? extends TunnelMonitoringTypeBase> monitorProtocol = ITMConstants.DEFAULT_MONITOR_PROTOCOL;
163 for (DPNTEPsInfo dpn : cfgdDpnList) {
164 LOG.trace("processing dpn {}" , dpn);
165 if (dpn.getTunnelEndPoints() != null && !dpn.getTunnelEndPoints().isEmpty()) {
166 for (TunnelEndPoints tep : dpn.getTunnelEndPoints()) {
167 for (TzMembership zone: tep.getTzMembership()) {
168 createTunnelsFromOVSinTransportZone(zone.getZoneName(), dpn, tep, idManagerService,
169 futures, transaction, dataBroker);
176 private static void createTunnelsFromOVSinTransportZone(String zoneName, DPNTEPsInfo dpn, TunnelEndPoints tep,
177 IdManagerService idManagerService,
178 List<ListenableFuture<Void>> futures,
179 WriteTransaction transaction, DataBroker dataBroker) {
180 InstanceIdentifier<TransportZone> tzonePath = InstanceIdentifier.builder(TransportZones.class)
181 .child(TransportZone.class, new TransportZoneKey(zoneName)).build();
182 Optional<TransportZone> transportZoneOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
183 tzonePath, dataBroker);
184 if (transportZoneOptional.isPresent()) {
185 TransportZone transportZone = transportZoneOptional.get();
186 //do we need to check tunnel type?
187 if (transportZone.getSubnets() != null && !transportZone.getSubnets().isEmpty()) {
188 for (Subnets sub : transportZone.getSubnets()) {
189 if (sub.getDeviceVteps() != null && !sub.getDeviceVteps().isEmpty()) {
190 for (DeviceVteps hwVtepDS : sub.getDeviceVteps()) {
191 //dont mesh if hwVteps and OVS-tep have same ip-address
192 if (hwVtepDS.getIpAddress().equals(tep.getIpAddress())) {
195 final String cssID = dpn.getDPNID().toString();
196 String nodeId = hwVtepDS.getNodeId();
197 boolean useOfTunnel = ItmUtils.falseIfNull(tep.isOptionOfTunnel());
198 LOG.trace("wire up {} and {}",tep, hwVtepDS);
199 if (!wireUp(dpn.getDPNID(), tep.getPortname(), sub.getVlanId(),
200 tep.getIpAddress(), useOfTunnel, nodeId, hwVtepDS.getIpAddress(),
201 tep.getSubnetMask(), sub.getGatewayIp(), sub.getPrefix(),
202 transportZone.getTunnelType(), false, monitorProtocol, monitorInterval,
203 idManagerService, dataBroker, futures, transaction)) {
204 LOG.error("Unable to build tunnel {} -- {}",
205 tep.getIpAddress(), hwVtepDS.getIpAddress());
208 LOG.trace("wire up {} and {}", hwVtepDS,tep);
209 if (!wireUp(hwVtepDS.getTopologyId(), hwVtepDS.getNodeId(), hwVtepDS.getIpAddress(),
210 cssID, tep.getIpAddress(), sub.getPrefix(), sub.getGatewayIp(),
211 tep.getSubnetMask(), transportZone.getTunnelType(), false, monitorProtocol,
212 monitorInterval, idManagerService, dataBroker, futures, transaction)) {
213 LOG.error("Unable to build tunnel {} -- {}",
214 hwVtepDS.getIpAddress(), tep.getIpAddress());
224 private static void tunnelsFromhWVtep(List<HwVtep> cfgdHwVteps, IdManagerService idManagerService,
225 List<ListenableFuture<Void>> futures, WriteTransaction transaction,
226 DataBroker dataBroker) {
227 for (HwVtep hwTep : cfgdHwVteps) {
228 InstanceIdentifier<TransportZone> tzonePath = InstanceIdentifier.builder(TransportZones.class)
229 .child(TransportZone.class, new TransportZoneKey(hwTep.getTransportZone())).build();
230 Optional<TransportZone> transportZoneOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
231 tzonePath, dataBroker);
232 Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
233 Boolean monitorEnabled = ItmUtils.readMonitoringStateFromCache(dataBroker);
234 Class<? extends TunnelMonitoringTypeBase> monitorProtocol = ITMConstants.DEFAULT_MONITOR_PROTOCOL;
235 if (transportZoneOptional.isPresent()) {
236 TransportZone tzone = transportZoneOptional.get();
237 //do we need to check tunnel type?
238 if (tzone.getSubnets() != null && !tzone.getSubnets().isEmpty()) {
239 for (Subnets sub : tzone.getSubnets()) {
240 if (sub.getDeviceVteps() != null && !sub.getDeviceVteps().isEmpty()) {
241 for (DeviceVteps hwVtepDS : sub.getDeviceVteps()) {
242 if (hwVtepDS.getIpAddress().equals(hwTep.getHwIp())) {
243 continue;//dont mesh with self
245 LOG.trace("wire up {} and {}",hwTep, hwVtepDS);
246 if (!wireUp(hwTep.getTopoId(), hwTep.getNodeId(), hwTep.getHwIp(),
247 hwVtepDS.getNodeId(), hwVtepDS.getIpAddress(), hwTep.getIpPrefix(),
248 hwTep.getGatewayIP(), sub.getPrefix(), tunType,false,monitorProtocol,
249 monitorInterval, idManagerService, dataBroker, futures, transaction)) {
250 LOG.error("Unable to build tunnel {} -- {}",
251 hwTep.getHwIp(), hwVtepDS.getIpAddress());
254 LOG.trace("wire up {} and {}", hwVtepDS,hwTep);
255 if (!wireUp(hwTep.getTopoId(), hwVtepDS.getNodeId(), hwVtepDS.getIpAddress(),
256 hwTep.getNodeId(), hwTep.getHwIp(), sub.getPrefix(), sub.getGatewayIp(),
257 hwTep.getIpPrefix(), tunType, false, monitorProtocol, monitorInterval,
258 idManagerService, dataBroker, futures, transaction)) {
259 LOG.error("Unable to build tunnel {} -- {}",
260 hwVtepDS.getIpAddress(), hwTep.getHwIp());
264 if (sub.getVteps() != null && !sub.getVteps().isEmpty()) {
265 for (Vteps vtep : sub.getVteps()) {
266 if (vtep.getIpAddress().equals(hwTep.getHwIp())) {
270 String cssID = vtep.getDpnId().toString();
271 LOG.trace("wire up {} and {}",hwTep, vtep);
272 if (!wireUp(hwTep.getTopoId(), hwTep.getNodeId(), hwTep.getHwIp(), cssID,
273 vtep.getIpAddress(), hwTep.getIpPrefix(), hwTep.getGatewayIP(),
274 sub.getPrefix(), tunType,false, monitorProtocol, monitorInterval,
275 idManagerService, dataBroker, futures, transaction)) {
276 LOG.error("Unable to build tunnel {} -- {}",
277 hwTep.getHwIp(), vtep.getIpAddress());
280 LOG.trace("wire up {} and {}", vtep,hwTep);
281 boolean useOfTunnel = ItmUtils.falseIfNull(vtep.isOptionOfTunnel());
282 if (!wireUp(vtep.getDpnId(), vtep.getPortname(), sub.getVlanId(), vtep.getIpAddress(),
283 useOfTunnel, hwTep.getNodeId(),hwTep.getHwIp(),sub.getPrefix(),
284 sub.getGatewayIp(),hwTep.getIpPrefix(), tunType,false,monitorProtocol,
285 monitorInterval, idManagerService, dataBroker, futures, transaction)) {
297 //for tunnels from TOR device
298 private static boolean wireUp(String topoId, String srcNodeid, IpAddress srcIp, String dstNodeId, IpAddress dstIp,
299 IpPrefix srcSubnet, IpAddress gwIp, IpPrefix dstSubnet,
300 Class<? extends TunnelTypeBase> tunType,Boolean monitorEnabled,
301 Class<? extends TunnelMonitoringTypeBase> monitorProtocol, Integer monitorInterval,
302 IdManagerService idManagerService, DataBroker dataBroker,
303 List<ListenableFuture<Void>> futures, WriteTransaction transaction) {
304 IpAddress gatewayIpObj = new IpAddress("0.0.0.0".toCharArray());
305 IpAddress gwyIpAddress = srcSubnet.equals(dstSubnet) ? gatewayIpObj : gwIp;
306 String parentIf = ItmUtils.getHwParentIf(topoId, srcNodeid);
307 String tunTypeStr = tunType.getName();
308 String tunnelIfName = ItmUtils.getTrunkInterfaceName(idManagerService, parentIf,
309 new String(srcIp.getValue()), new String(dstIp.getValue()), tunTypeStr);
310 LOG.debug(" Creating ExternalTrunk Interface with parameters Name - {}, parent I/f name - {}, "
311 + "source IP - {}, destination IP - {} gateway IP - {}", tunnelIfName, parentIf, srcIp,
312 dstIp, gwyIpAddress);
313 Interface hwTunnelIf = ItmUtils.buildHwTunnelInterface(tunnelIfName,
314 String.format("%s %s", tunType.getName(), "Trunk Interface"), true, topoId, srcNodeid, tunType, srcIp,
315 dstIp, gwyIpAddress, monitorEnabled, monitorProtocol, monitorInterval);
316 InstanceIdentifier<Interface> ifIID = InstanceIdentifier.builder(Interfaces.class)
317 .child(Interface.class, new InterfaceKey(tunnelIfName)).build();
318 LOG.trace(" Writing Trunk Interface to Config DS {}, {} ", ifIID, hwTunnelIf);
319 ItmUtils.itmCache.addInterface(hwTunnelIf);
320 transaction.merge(LogicalDatastoreType.CONFIGURATION, ifIID, hwTunnelIf, true);
321 // also update itm-state ds?
322 InstanceIdentifier<ExternalTunnel> path = InstanceIdentifier.create(ExternalTunnelList.class)
323 .child(ExternalTunnel.class, new ExternalTunnelKey(getExternalTunnelKey(dstNodeId),
324 getExternalTunnelKey(srcNodeid), tunType));
325 ExternalTunnel tnl = ItmUtils.buildExternalTunnel(getExternalTunnelKey(srcNodeid),
326 getExternalTunnelKey(dstNodeId), tunType, tunnelIfName);
327 transaction.merge(LogicalDatastoreType.CONFIGURATION, path, tnl, true);
328 ItmUtils.itmCache.addExternalTunnel(tnl);
332 //for tunnels from OVS
333 private static boolean wireUp(BigInteger dpnId, String portname, Integer vlanId, IpAddress srcIp,
334 Boolean remoteIpFlow, String dstNodeId, IpAddress dstIp, IpPrefix srcSubnet,
335 IpAddress gwIp, IpPrefix dstSubnet, Class<? extends TunnelTypeBase> tunType,
336 Boolean monitorEnabled, Class<? extends TunnelMonitoringTypeBase> monitorProtocol,
337 Integer monitorInterval, IdManagerService idManagerService, DataBroker dataBroker,
338 List<ListenableFuture<Void>> futures, WriteTransaction transaction) {
339 IpAddress gatewayIpObj = new IpAddress("0.0.0.0".toCharArray());
340 IpAddress gwyIpAddress = srcSubnet.equals(dstSubnet) ? gatewayIpObj : gwIp;
341 String parentIf = ItmUtils.getInterfaceName(dpnId, portname, vlanId);
342 String tunTypeStr = tunType.getName();
343 String tunnelIfName = ItmUtils.getTrunkInterfaceName(idManagerService, parentIf,
344 new String(srcIp.getValue()), new String(dstIp.getValue()), tunTypeStr);
345 LOG.debug(" Creating ExternalTrunk Interface with parameters Name - {}, parent I/f name - {}, "
346 + "source IP - {}, destination IP - {} gateway IP - {}", tunnelIfName, parentIf, srcIp,
347 dstIp, gwyIpAddress);
348 Interface extTunnelIf = ItmUtils.buildTunnelInterface(dpnId, tunnelIfName,
349 String.format("%s %s", tunType.getName(), "Trunk Interface"), true, tunType, srcIp, dstIp, gwyIpAddress,
350 vlanId, false,monitorEnabled, monitorProtocol, monitorInterval, remoteIpFlow, null);
351 InstanceIdentifier<Interface> ifIID = InstanceIdentifier.builder(Interfaces.class).child(Interface.class,
352 new InterfaceKey(tunnelIfName)).build();
353 LOG.trace(" Writing Trunk Interface to Config DS {}, {} ", ifIID, extTunnelIf);
354 transaction.merge(LogicalDatastoreType.CONFIGURATION, ifIID, extTunnelIf, true);
355 ItmUtils.itmCache.addInterface(extTunnelIf);
356 InstanceIdentifier<ExternalTunnel> path = InstanceIdentifier.create(ExternalTunnelList.class)
357 .child(ExternalTunnel.class, new ExternalTunnelKey(getExternalTunnelKey(dstNodeId),
358 dpnId.toString(), tunType));
359 ExternalTunnel tnl = ItmUtils.buildExternalTunnel(dpnId.toString(),
360 getExternalTunnelKey(dstNodeId),
361 tunType, tunnelIfName);
362 transaction.merge(LogicalDatastoreType.CONFIGURATION, path, tnl, true);
363 ItmUtils.itmCache.addExternalTunnel(tnl);
367 static String getExternalTunnelKey(String nodeid) {
368 if (nodeid.indexOf("physicalswitch") > 0) {
369 nodeid = nodeid.substring(0, nodeid.indexOf("physicalswitch") - 1);