Open Kilda Java Documentation
AvailableNetwork.java
Go to the documentation of this file.
1 /* Copyright 2018 Telstra Open Source
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package org.openkilda.pce.model;
17 
18 import static org.openkilda.pce.Utils.safeAsInt;
19 
21 
22 import org.neo4j.driver.v1.AccessMode;
23 import org.neo4j.driver.v1.Driver;
24 import org.neo4j.driver.v1.Session;
25 import org.neo4j.driver.v1.StatementResult;
26 import org.neo4j.driver.v1.Value;
27 import org.neo4j.driver.v1.Values;
28 import org.neo4j.driver.v1.types.Relationship;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 import java.util.Set;
39 import java.util.stream.Collectors;
40 
47 public class AvailableNetwork {
48 
49  private static final Logger logger = LoggerFactory.getLogger(AvailableNetwork.class);
50 
51  private HashMap<SwitchId, SimpleSwitch> switches = new HashMap<>(); // key = DPID
52 
53  private final Driver driver;
54 
58  public AvailableNetwork(Driver driver, boolean ignoreBandwidth, long requestedBandwidth) {
59  this.driver = driver;
60 
61  buildNetwork(ignoreBandwidth, requestedBandwidth);
62  }
63 
67  public AvailableNetwork(Driver driver) {
68  this.driver = driver;
69  }
70 
77  private SimpleSwitch initSwitch(SwitchId dpid) {
78  SimpleSwitch result = switches.get(dpid);
79  if (result == null) {
80  result = new SimpleSwitch(dpid);
81  switches.put(dpid, result);
82  }
83  return result;
84  }
85 
89  public AvailableNetwork addLink(SwitchId srcDpid, SwitchId dstDpid, int srcPort, int dstPort,
90  int cost, int latency) {
91  SimpleSwitch srcSwitch = initSwitch(srcDpid);
92  SimpleIsl isl = new SimpleIsl(srcDpid, dstDpid, srcPort, dstPort, cost, latency);
93  srcSwitch.addOutbound(isl);
94  if (cost == 0) {
95  logger.warn("Found ZERO COST ISL: {}", isl);
96  }
97  return this;
98  }
99 
100  public Map<SwitchId, SimpleSwitch> getSwitches() {
101  return switches;
102  }
103 
105  return switches.get(dpid);
106  }
107 
112  public Map<String, Integer> getCounts() {
113  Map<String, Integer> result = new HashMap<>();
114 
115  result.put("SWITCHES", switches.size());
116 
117  int neighbors = 0;
118  int islCount = 0;
119  for (SimpleSwitch sw : switches.values()) {
120  neighbors += sw.outbound.size();
121  for (Set<SimpleIsl> isls : sw.outbound.values()) {
122  islCount += isls.size();
123  }
124  }
125  result.put("NEIGHBORS", neighbors);
126  result.put("ISLS", islCount);
127 
128  return result;
129  }
130 
135  public void sanityCheck() {
136  /*
137  * Algorithm:
138  * 1) ensure there is only one link from a src to its neighbor (ie dst)
139  * 2) ensure there are no self loops (most things should still work, but could lead to bad
140  * behavior, like looping until depth is reached.
141  * 3) any negative costs?? remove for now
142  */
143  }
144 
150  public void reduceByCost() {
151  /*
152  * Algorithm:
153  * 1) Loop through all switches
154  * 2) For each switch, loop through its neighbors
155  * 3) Per neighbor, reduce the number of links to 1, based on cost.
156  */
157  for (SimpleSwitch sw : switches.values()) { // 1: each switch
158  if (sw.outbound.size() < 1) {
159  logger.warn("AvailableNetwork: Switch {} has NO OUTBOUND isls", sw.dpid);
160  continue;
161  }
162  for (Entry<SwitchId, Set<SimpleIsl>> linksEntry : sw.outbound.entrySet()) { // 2: each neighbor
163  Set<SimpleIsl> links = linksEntry.getValue();
164  if (links.size() <= 1) {
165  continue; // already at 1 or less
166  }
167 
168  SimpleIsl cheapestLink = links.stream()
169  .min(Comparator.comparingInt(SimpleIsl::getCost))
170  .get();
171 
172  sw.outbound.put(linksEntry.getKey(), Collections.singleton(cheapestLink));
173  }
174  }
175  }
176 
183  for (SimpleSwitch sw : switches.values()) {
184  if (sw.outbound.containsKey(sw.dpid)) {
185  sw.outbound.remove(sw.dpid);
186  }
187  }
188  return this;
189  }
190 
198  public void addIslsOccupiedByFlow(String flowId, boolean ignoreBandwidth, long flowBandwidth) {
199  String query = ""
200  + "MATCH (src:switch)-[fs:flow_segment{flowid: $flow_id}]->(dst:switch) "
201  + "MATCH (src)-[link:isl]->(dst) "
202  + "WHERE src.state = 'active' AND dst.state = 'active' AND link.status = 'active' "
203  + "AND link.src_port = fs.src_port AND link.dst_port = fs.dst_port "
204  + "AND ($ignore_bandwidth OR link.available_bandwidth + fs.bandwidth >= $requested_bandwidth) "
205  + "RETURN link";
206 
207  Value parameters = Values.parameters(
208  "flow_id", flowId,
209  "ignore_bandwidth", ignoreBandwidth,
210  "requested_bandwidth", flowBandwidth);
211 
212  List<Relationship> links = loadAvailableLinks(query, parameters);
213  addLinksFromRelationships(links);
214  }
215 
222  private void buildNetwork(boolean ignoreBandwidth, long flowBandwidth) {
223  String q = "MATCH (src:switch)-[link:isl]->(dst:switch) "
224  + " WHERE src.state = 'active' AND dst.state = 'active' AND link.status = 'active' "
225  + " AND src.name IS NOT NULL AND dst.name IS NOT NULL "
226  + " AND ($ignore_bandwidth OR link.available_bandwidth >= $requested_bandwidth) "
227  + " RETURN link";
228 
229  Value parameters = Values.parameters(
230  "ignore_bandwidth", ignoreBandwidth,
231  "requested_bandwidth", flowBandwidth
232  );
233 
234  List<Relationship> links = loadAvailableLinks(q, parameters);
235  addLinksFromRelationships(links);
236  }
237 
243  private List<Relationship> loadAvailableLinks(String query, Value parameters) {
244  logger.debug("Executing query for getting links to fill AvailableNetwork: {}", query);
245  try (Session session = driver.session(AccessMode.READ)) {
246  StatementResult queryResults = session.run(query, parameters);
247  return queryResults.list()
248  .stream()
249  .map(record -> record.get("link"))
250  .map(Value::asRelationship)
251  .collect(Collectors.toList());
252  }
253  }
254 
255  private void addLinksFromRelationships(List<Relationship> links) {
256  links.forEach(isl ->
257  addLink(new SwitchId(isl.get("src_switch").asString()),
258  new SwitchId(isl.get("dst_switch").asString()),
259  safeAsInt(isl.get("src_port")),
260  safeAsInt(isl.get("dst_port")),
261  safeAsInt(isl.get("cost")),
262  safeAsInt(isl.get("latency"))
263  ));
264  }
265 
266  @Override
267  public String toString() {
268  String result = "AvailableNetwork{";
269  StringBuilder sb = new StringBuilder();
270  for (SimpleSwitch sw : switches.values()) {
271  sb.append(sw);
272  }
273  result += sb.toString();
274  result += "\n}";
275  return result;
276 
277  }
278 }
AvailableNetwork(Driver driver, boolean ignoreBandwidth, long requestedBandwidth)
Map< SwitchId, SimpleSwitch > getSwitches()
AvailableNetwork addLink(SwitchId srcDpid, SwitchId dstDpid, int srcPort, int dstPort, int cost, int latency)
list result
Definition: plan-d.py:72
static int safeAsInt(Value val)
Definition: Utils.java:43
void addIslsOccupiedByFlow(String flowId, boolean ignoreBandwidth, long flowBandwidth)
SimpleSwitch getSimpleSwitch(SwitchId dpid)
SimpleSwitch addOutbound(SimpleIsl isl)