Skip to content

Commit

Permalink
updating the server so it works with sol-onos service (#5)
Browse files Browse the repository at this point in the history
* updating the server so it works with sol-onos service
  • Loading branch information
speeeday authored and progwriter committed Jan 15, 2018
1 parent 16317b2 commit 89550c2
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 31 deletions.
161 changes: 135 additions & 26 deletions server/server.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import argparse
import logging

import networkx as nx
import flask
import numpy
from attrdict import AttrDict
from flask import Flask, request, jsonify
from flask import abort
from flask import send_from_directory
from flask_compress import Compress
from sol.opt.composer import compose
from sol.opt import NetworkConfig, NetworkCaps
from sol.opt.composer import compose_apps
from sol.path.generate import generate_paths_ie
from sol.path.paths import PPTC
from sol.path.predicates import null_predicate
from sol.path.predicates import null_predicate, has_mbox_predicate
from sol.topology.topologynx import Topology
from sol.topology.traffic import TrafficClass
from sol.utils.const import EpochComposition, Fairness, Constraint, Objective, NODES, LINKS, ERR_UNKNOWN_MODE, MBOXES

from sol.opt.app import App

Expand All @@ -26,7 +29,18 @@
_json_pretty = False # whether to pretty print json or not (not == save space)
_gzip = True # whether to gzip the returned responses

_topology = None

# SET THIS TO THE TOPOLOGY WE ARE TESTING
#_topology = None
_topology = nx.DiGraph()
_topology.add_node(0, services='switch',resources={})
_topology.add_node(1, services='switch',resources={})
_topology.add_node(2, services='switch',resources={})
_topology.add_edge(0, 1, source=0, target=1, resources={'bw': 10000})
_topology.add_edge(1, 0, source=1, target=0, resources={'bw': 10000})
_topology.add_edge(2, 1, source=2, target=1, resources={'bw': 10000})
_topology.add_edge(1, 2, source=1, target=2, resources={'bw': 10000})
_topology = Topology(u'NoName', _topology)

_predicatedict = {
'null': null_predicate,
Expand Down Expand Up @@ -62,8 +76,34 @@ def hi():
"""
return u"Hello, this is SOL API version {}".format(__API_VERSION)


@app.route('/api/v1/compose_apps', methods=['POST'])
def toConstraint(con):
if con == u'route_all':
return [Constraint.ROUTE_ALL, [], {}]
elif con == u'allocate_flow':
return [Constraint.ALLOCATE_FLOW, [], {}] #CHECK IF THIS CASE IS RIGHT
elif con == u'req_all_links':
return [Constraint.REQ_ALL_LINKS, [], {}]
elif con == u'req_all_nodes':
return [Constraint.REQ_ALL_NODES, [], {}]
elif con == u'req_some_links':
return [Constraint.REQ_SOME_LINKS, [], {}]
elif con == u'req_some_nodes':
return [Constraint.REQ_SOME_NODES, [], {}]
elif con == u'cap_links':
return [Constraint.CAP_LINKS, [], {}]
elif con == u'cap_nodes':
return [Constraint.CAP_NODES, [], {}]
elif con == u'fix_path':
return [Constraint.FIX_PATHS, [], {}]
elif con == u'mindiff':
return [Constraint.MINDIFF, [], {}]
elif con == u'node_budget':
return [Constraint.NODE_BUDGET, [], {}]
else:
logger.debug("Received Unknown Constraint for Map: " + str(con))
return None

@app.route('/api/v1/compose', methods=['POST'])
def composeview():
"""
Create a new composed opimization, solve and return the
Expand All @@ -75,7 +115,14 @@ def composeview():
data = request.get_json()
logger.debug(data)
apps_json = data['apps']
topology = Topology.from_json(data['topology'])
# THIS IS PARSING TOPOLOGY INCORRECTLY

# topology = Topology.from_json(data['topology'])
topology = _topology
print "Topology from POST Request"
print data['topology']
print "Parsed topology using json_graph.node_link_graph()"
print topology.to_json()
except KeyError: # todo: is this right exception?
abort(400)

Expand All @@ -98,29 +145,85 @@ def composeview():
else:
objective = (aj.objective.name, aj.objective.resource)

# resource_cost
resource_cost = {r.resource: r.cost for r in map(AttrDict, aj.resource_costs)}
apps.append(App(pptc, list(aj.constraints), resource_cost, objective, name=aj.id))
fairness_mode = data.get('fairness', 'weighted')
opt = compose(apps, topology, obj_mode=fairness_mode,
globalcaps=[AttrDict(resource=r, cap=1) for r in resource_cost.keys()])
logger.debug("Objective: " + str(objective))

for r in map(AttrDict, aj.resource_costs):
logger.debug("Resource type: " + str(r.resource))
# [TODO: NEED TO UNDERSTAND THE DIFFERENCE BETWEEN THE MODES (NODES/MBOXES/LINKS)]
# resource : (mode, cost_value, cost_function)
resource_cost = {r.resource: (LINKS, r.cost, 0) for r in map(AttrDict, aj.resource_costs)}
logger.debug("Printing Constraints: " + str(list(aj.constraints)))
wrap_constraints = []
for con in list(aj.constraints):
if con == u'route_all':
wrap_constraints.append([Constraint.ROUTE_ALL, [], {}])
elif con == u'allocate_flow':
do_nothing = 1 # do nothing here because allocate_flow is called when opt is initialized
elif con == u'req_all_links':
wrap_constraints.append([Constraint.REQ_ALL_LINKS, [], {}])
elif con == u'req_all_nodes':
wrap_constraints.append([Constraint.REQ_ALL_NODES, [], {}])
elif con == u'req_some_links':
wrap_constraints.append([Constraint.REQ_SOME_LINKS, [], {}])
elif con == u'req_some_nodes':
wrap_constraints.append([Constraint.REQ_SOME_NODES, [], {}])
elif con == u'cap_links':
wrap_constraints.append([Constraint.CAP_LINKS, [], {}])
elif con == u'cap_nodes':
wrap_constraints.append([Constraint.CAP_NODES, [], {}])
elif con == u'fix_path':
wrap_constraints.append([Constraint.FIX_PATHS, [], {}])
elif con == u'mindiff':
wrap_constraints.append([Constraint.MINDIFF, [], {}])
elif con == u'node_budget':
wrap_constraints.append([Constraint.NODE_BUDGET, [], {}])
else:
logger.debug("Received Unknown Constraint for Map: " + str(con))

wrap_objectives = []
if objective[0] == u'minlinkload':
wrap_objectives.append(Objective.MIN_LINK_LOAD)
elif objective[0] == u'minnodeload':
wrap_objectives.append(Objective.MIN_NODE_LOAD)
elif objective[0] == u'minlatency':
wrap_objectives.append(Objective.MIN_LATENCY)
elif objective[0] == u'maxflow':
wrap_objectives.append(Objective.MAX_FLOW)
elif objective[0] == u'minenablednodes':
wrap_objectives.append(Objective.MIN_ENABLED_NODES)
else:
logger.debug("Couldn't find Objective Name")
obj_name = None
wrap_objectives.append([objective[1]]) #*args
wrap_objectives.append({}) #**kwargs

apps.append(App(pptc, wrap_constraints, resource_cost, wrap_objectives, name=aj.id))
ncaps = NetworkCaps(topology)
for r in resource_cost.keys():
ncaps.add_cap(r,None,1)
opt = compose_apps(apps, topology, NetworkConfig(networkcaps=ncaps), epoch_mode=EpochComposition.WORST, fairness=Fairness.WEIGHTED, weights = None)
opt.solve()
result = []
for app in apps:
result_app = {"app": app.name, "tcs": []}
result_pptc = opt.get_paths(0)
for tc in app.pptc:
obj = {
"tcid": tc.ID,
"paths": []
}
for p in result_pptc[tc]:
if p.flow_fraction() != 0:
obj["paths"].append({
"nodes": p.nodes().tolist(),
"fraction": p.flow_fraction()
})
result_app["tcs"].append(obj)
it = (app.pptc).tcs()
while True:
try:
tc = it.next()
obj = {
"tcid": tc.ID,
"paths": []
}
for p in result_pptc.paths(tc):
if p.flow_fraction() != 0:
obj["paths"].append({
"nodes": p.nodes().tolist(),
"fraction": p.flow_fraction()
})
result_app["tcs"].append(obj)
except StopIteration:
break
result.append(result_app)
logger.debug(result)
return jsonify(result)
Expand All @@ -132,6 +235,10 @@ def topology():
Set or return the stored topology
"""

#TODO: make sure the mbox nodes are identified for the topology object

logger.debug("Setting global variables _topology and _paths")
global _topology, _paths
if request.method == 'GET':
if _topology is None:
Expand All @@ -140,12 +247,14 @@ def topology():
elif request.method == 'POST':
data = request.get_json()
logger.debug(data)
_topology = Topology.from_json(data)
logging.info('Topology read successfully')
# _topology = Topology.from_json(data)
logging.info('Topology read successfully as:')
logging.info(_topology.to_json())
_paths = {}
for s in _topology.nodes():
_paths[s] = {}
for t in _topology.nodes():
# this is where the predicate needs to be defined TODO
_paths[s][t] = list(generate_paths_ie(s, t, _topology, null_predicate, 100, 5))
return ""
else:
Expand Down
16 changes: 14 additions & 2 deletions src/sol/opt/composer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ from sol.topology.topologynx cimport Topology
from sol.path.paths import PPTC
from sol.path.paths cimport PPTC

from sol.utils.const import EpochComposition, Fairness, NODES, LINKS, ERR_UNKNOWN_MODE, MBOXES
from sol.utils.const import EpochComposition, Fairness, Objective, NODES, LINKS, ERR_UNKNOWN_MODE, MBOXES
from sol.utils.exceptions import InvalidConfigException
from sol.utils.logger import logger

Expand Down Expand Up @@ -50,6 +50,8 @@ cpdef compose_apps(apps, Topology topo, network_config, epoch_mode=EpochComposit
rset.update(app.resource_cost.keys())
# print (rset)
for r in rset:
logger.debug("App Resource Cost List: " + str(r))
logger.debug(str([app.resource_cost[r] for app in apps if r in app.resource_cost]))
modes, cost_vals, cost_funcs = zip(*[app.resource_cost[r] for app in apps if r in app.resource_cost])
# Make sure all the modes agree for a given resource
assert len(set(modes)) == 1
Expand Down Expand Up @@ -87,10 +89,20 @@ cpdef compose_apps(apps, Topology topo, network_config, epoch_mode=EpochComposit
# Add objectives
objs = []
for app in apps:
kwargs = app.obj[2].copy()
logger.debug("Currently on app: " + str(app))
logger.debug("App Objectives: " + str(app.obj))

kwargs = app.obj[2].copy() #THIS COPY CALL KEEPS SEGFAULTING
logger.debug("Just copied kwargs")
kwargs.update(dict(varname=app.name, tcs=app.obj_tc))
logger.debug("Just finished update")
epoch_objs = opt.add_single_objective(app.obj[0], *app.obj[1], **kwargs)
logger.debug("Added Objective")
objs.append(epoch_objs)


logger.debug("Composing Objectives")

opt.compose_objectives(array(objs), epoch_mode, fairness, weights)
return opt

9 changes: 7 additions & 2 deletions src/sol/opt/gurobiwrapper.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,9 @@ cdef class OptimizationGurobi:
:param varname: now to name the objective variable. If None, a default will be provided
:return: A list of gurobi variables (one per each epoch)
"""
logger.debug("Min Link Load: ")


return self._min_load(resource, tcs, varname)

cpdef max_flow(self, tcs=None, varname=None):
Expand Down Expand Up @@ -1047,6 +1050,8 @@ cdef class OptimizationGurobi:
:param kwargs: keyword arguments to be passed to objective generation function
:return:
"""
logger.debug("add Single Objective")

epoch_objs = None
if name == Objective.MIN_LINK_LOAD:
epoch_objs = self.min_link_load(*args, **kwargs)
Expand All @@ -1069,8 +1074,8 @@ cdef class OptimizationGurobi:
"""
for c in app.constraints:
args, kwargs = c[1], c[2]
# if c[0] == Constraint.ALLOCATE_FLOW:
# self.allocate_flow(*args, **kwargs)
# if c[0] == Constraint.ALLOCATE_FLOW:
# self.allocate_flow(*args, **kwargs)
if c[0] == Constraint.ROUTE_ALL:
self.route_all(*args, **kwargs)
elif c[0] == Constraint.CAP_LINKS or c[0] == Constraint.CAP_NODES:
Expand Down
2 changes: 1 addition & 1 deletion src/sol/utils/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Constraint(Enum):
FIX_PATHS = u'fix_path'
MINDIFF = u'mindiff'
NODE_BUDGET = u'node_budget'

ALLOCATE_FLOW = u'allocate_flow'


class BinType(Enum):
Expand Down

0 comments on commit 89550c2

Please sign in to comment.