package org.onosproject.segmentrouting.config; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.onosproject.net.DeviceId; import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; /** * Manages additional configuration for switches configured as Segment Routers. */ public class SegmentRouterConfig extends SwitchConfig { protected static final Logger log = LoggerFactory .getLogger(SegmentRouterConfig.class); private String routerIp; private String routerMac; private int nodeSid; private boolean isEdgeRouter; private List adjacencySids; private List subnets; public static final String ROUTER_IP = "routerIp"; public static final String ROUTER_MAC = "routerMac"; public static final String NODE_SID = "nodeSid"; public static final String ADJACENCY_SIDS = "adjacencySids"; public static final String SUBNETS = "subnets"; public static final String ISEDGE = "isEdgeRouter"; private static final int SRGB_MAX = 1000; /** * Parses and validates the additional configuration parameters applicable * to segment routers. * * @param swc switch configuration */ public SegmentRouterConfig(SwitchConfig swc) { this.setName(swc.getName()); this.setDpid(swc.getDpid()); this.setType(swc.getType()); this.setLatitude(swc.getLatitude()); this.setLongitude(swc.getLongitude()); this.setParams(swc.getParams()); this.setAllowed(swc.isAllowed()); publishAttributes = new ConcurrentHashMap(); adjacencySids = new ArrayList(); subnets = new ArrayList(); parseParams(); validateParams(); setPublishAttributes(); } /** * Returns the configured segment router IP address. * * @return ip address in string format */ public String getRouterIp() { return routerIp; } public void setRouterIp(String routerIp) { this.routerIp = routerIp; } /** * Returns the configured segment router mac address. * * @return mac address in string format */ public String getRouterMac() { return routerMac; } public void setRouterMac(String routerMac) { this.routerMac = routerMac; } /** * Returns the configured sID for a segment router. * * @return segment identifier */ public int getNodeSid() { return nodeSid; } public void setNodeSid(int nodeSid) { this.nodeSid = nodeSid; } /** * Returns the flag that indicates the configured segment router * is edge or backbone router. * * @return boolean */ public boolean isEdgeRouter() { return isEdgeRouter; } public void setIsEdgeRouter(boolean isEdge) { this.isEdgeRouter = isEdge; } /** * Class representing segment router adjacency identifier. */ public static class AdjacencySid { private int adjSid; private List ports; public AdjacencySid(int adjSid, List ports) { this.ports = ports; this.adjSid = adjSid; } /** * Returns the list of ports part of a segment * router adjacency identifier. * * @return list of integers */ public List getPorts() { return ports; } public void setPorts(List ports) { this.ports = ports; } /** * Returns the configured adjacency id of a segment router. * * @return integer */ public int getAdjSid() { return adjSid; } public void setAdjSid(int adjSid) { this.adjSid = adjSid; } } /** * Returns the configured adjacent segment IDs for a segment router. * * @return list of adjacency identifier */ public List getAdjacencySids() { return adjacencySids; } public void setAdjacencySids(List adjacencySids) { this.adjacencySids = adjacencySids; } /** * Class representing a subnet attached to a segment router. */ public static class Subnet { private int portNo; private String subnetIp; public Subnet(int portNo, String subnetIp) { this.portNo = portNo; this.subnetIp = subnetIp; } /** * Returns the port number of segment router on * which subnet is attached. * * @return integer */ public int getPortNo() { return portNo; } public void setPortNo(int portNo) { this.portNo = portNo; } /** * Returns the configured subnet address. * * @return subnet ip address in string format */ public String getSubnetIp() { return subnetIp; } public void setSubnetIp(String subnetIp) { this.subnetIp = subnetIp; } } /** * Returns the configured subnets for a segment router. * * @return list of subnets */ public List getSubnets() { return subnets; } public void setSubnets(List subnets) { this.subnets = subnets; } // ******************** // Helper methods // ******************** private void parseParams() { if (params == null) { throw new NetworkConfigException.ParamsNotSpecified(name); } Set> m = params.entrySet(); for (Entry e : m) { String key = e.getKey(); JsonNode j = e.getValue(); if (key.equals("routerIp")) { setRouterIp(j.asText()); } else if (key.equals("routerMac")) { setRouterMac(j.asText()); } else if (key.equals("nodeSid")) { setNodeSid(j.asInt()); } else if (key.equals("isEdgeRouter")) { setIsEdgeRouter(j.asBoolean()); } else if (key.equals("adjacencySids") || key.equals("subnets")) { getInnerParams(j, key); } else { throw new UnknownSegmentRouterConfig(key, dpid); } } } private void getInnerParams(JsonNode j, String innerParam) { Iterator innerList = j.elements(); while (innerList.hasNext()) { Iterator> f = innerList.next().fields(); int portNo = -1; int adjSid = -1; String subnetIp = null; List ports = null; while (f.hasNext()) { Entry fe = f.next(); if (fe.getKey().equals("portNo")) { portNo = fe.getValue().asInt(); } else if (fe.getKey().equals("adjSid")) { adjSid = fe.getValue().asInt(); } else if (fe.getKey().equals("subnetIp")) { subnetIp = fe.getValue().asText(); } else if (fe.getKey().equals("ports")) { if (fe.getValue().isArray()) { Iterator i = fe.getValue().elements(); ports = new ArrayList(); while (i.hasNext()) { ports.add(i.next().asInt()); } } } else { throw new UnknownSegmentRouterConfig(fe.getKey(), dpid); } } if (innerParam.equals("adjacencySids")) { AdjacencySid ads = new AdjacencySid(adjSid, ports); adjacencySids.add(ads); } else { Subnet sip = new Subnet(portNo, subnetIp); subnets.add(sip); } } } private void validateParams() { if (routerIp == null) { throw new IpNotSpecified(dpid); } if (routerMac == null) { throw new MacNotSpecified(dpid); } if (isEdgeRouter && subnets.isEmpty()) { throw new SubnetNotSpecifiedInEdgeRouter(dpid); } if (!isEdgeRouter && !subnets.isEmpty()) { throw new SubnetSpecifiedInBackboneRouter(dpid); } if (nodeSid > SRGB_MAX) { throw new NodeLabelNotInSRGB(nodeSid, dpid); } for (AdjacencySid as : adjacencySids) { int label = as.getAdjSid(); List plist = as.getPorts(); if (label <= SRGB_MAX) { throw new AdjacencyLabelInSRGB(label, dpid); } if (plist.size() <= 1) { throw new AdjacencyLabelNotEnoughPorts(label, dpid); } } // TODO more validations } /** * Setting publishAttributes implies that this is the configuration that * will be added to Topology.Switch object before it is published on the * channel to other controller instances. */ private void setPublishAttributes() { publishAttributes.put(ROUTER_IP, routerIp); publishAttributes.put(ROUTER_MAC, routerMac); publishAttributes.put(NODE_SID, String.valueOf(nodeSid)); publishAttributes.put(ISEDGE, String.valueOf(isEdgeRouter)); ObjectMapper mapper = new ObjectMapper(); try { publishAttributes.put(ADJACENCY_SIDS, mapper.writeValueAsString(adjacencySids)); publishAttributes.put(SUBNETS, mapper.writeValueAsString(subnets)); } catch (JsonProcessingException e) { log.error("Error while writing SR config: {}", e.getCause()); } catch (IOException e) { log.error("Error while writing SR config: {}", e.getCause()); } } // ******************** // Exceptions // ******************** public static class IpNotSpecified extends RuntimeException { private static final long serialVersionUID = -3001502553646331686L; public IpNotSpecified(DeviceId dpid) { super(); log.error("Router IP address not specified for SR config dpid:{}", dpid); } } public static class MacNotSpecified extends RuntimeException { private static final long serialVersionUID = -5850132094884129179L; public MacNotSpecified(DeviceId dpid) { super(); log.error("Router Mac address not specified for SR config dpid:{}", dpid); } } public static class UnknownSegmentRouterConfig extends RuntimeException { private static final long serialVersionUID = -5750132094884129179L; public UnknownSegmentRouterConfig(String key, DeviceId dpid) { super(); log.error("Unknown Segment Router config {} in dpid: {}", key, dpid); } } public static class SubnetNotSpecifiedInEdgeRouter extends RuntimeException { private static final long serialVersionUID = -5855458472668581268L; public SubnetNotSpecifiedInEdgeRouter(DeviceId dpid) { super(); log.error("Subnet was not specified for edge router in dpid: {}", dpid); } } public static class SubnetSpecifiedInBackboneRouter extends RuntimeException { private static final long serialVersionUID = 1L; public SubnetSpecifiedInBackboneRouter(DeviceId dpid) { super(); log.error("Subnet was specified in backbone router in dpid: {}", dpid); } } public static class NodeLabelNotInSRGB extends RuntimeException { private static final long serialVersionUID = -8482670903748519526L; public NodeLabelNotInSRGB(int label, DeviceId dpid) { super(); log.error("Node sif {} specified in not in global label-base " + "in dpid: {}", label, dpid); } } public static class AdjacencyLabelInSRGB extends RuntimeException { private static final long serialVersionUID = -8482670903748519526L; public AdjacencyLabelInSRGB(int label, DeviceId dpid) { super(); log.error("Adjaceny label {} specified from global label-base " + "in dpid: {}", label, dpid); } } public static class AdjacencyLabelNotEnoughPorts extends RuntimeException { private static final long serialVersionUID = -8482670903748519526L; public AdjacencyLabelNotEnoughPorts(int label, DeviceId dpid) { super(); log.error("Adjaceny label {} must be specified for at least 2 ports. " + "Adjacency labels for single ports are auto-generated " + "in dpid: {}", label, dpid); } } }