def save_end2end_delay(edges, links): flow_delays = {} for edge in edges: for flow in edge.flows: if flow.id not in flow_delays: flow_delays[flow.id] = 0 # adding vnf_delays of destinations flow_delays[flow.id] += edge.dest.component.vnf_delay # adding path delay ,path delays are always the shortest paths and hence the same, so just adding the one at 0th index. flow_delays[flow.id] += sp.path_delay(links, edge.paths[0]) return flow_delays
def save_heuristic_variables(result, changed_instances, instances, edges, nodes, links): # save placement result["placement"] = {"vnfs": [], "vlinks": []} for i in instances: vnf = { "name": i.component.name, "node": i.location, "image": i.component.config } result["placement"]["vnfs"].append(vnf) result["metrics"]["num_instances"] = len(result["placement"]["vnfs"]) for e in edges: vlink = { "src_vnf": e.source.component.name, "src_node": e.source.location, "dest_vnf": e.dest.component.name, "dest_node": e.dest.location } result["placement"]["vlinks"].append(vlink) # node capacity violations result["placement"]["cpu_oversub"] = [] result["placement"]["mem_oversub"] = [] max_cpu, max_mem = 0, 0 for v in nodes.ids: over_cpu = sum(i.consumed_cpu() for i in instances if i.location == v) - nodes.cpu[v] if over_cpu > 0: result["placement"]["cpu_oversub"].append({"node": v}) if over_cpu > max_cpu: max_cpu = over_cpu over_mem = sum(i.consumed_mem() for i in instances if i.location == v) - nodes.mem[v] if over_mem > 0: result["placement"]["mem_oversub"].append({"node": v}) if over_mem > max_mem: max_mem = over_mem result["metrics"]["max_cpu_oversub"] = max_cpu result["metrics"]["max_mem_oversub"] = max_mem # consumed node resources result["placement"]["alloc_node_res"] = [] for i in instances: resources = { "name": i.component.name, "node": i.location, "cpu": i.consumed_cpu(), "mem": i.consumed_mem() } result["placement"]["alloc_node_res"].append(resources) # changed instances (compared to previous embedding) result["metrics"]["changed"] = [] for i in changed_instances: result["metrics"]["changed"].append({ "name": i.component.name, "node": i.location }) result["metrics"]["num_changed"] = len(result["metrics"]["changed"]) # edge and link data rate, used links result["placement"]["flows"] = [] result["metrics"]["path_delays"] = [] result["metrics"]["vnf_delays"] = [] result["metrics"]["total_path_delay"] = 0 result["metrics"]["total_vnf_delay"] = 0 result['metrics']["total_delay"] = 0 result["placement"]["links"] = [] consumed_dr = defaultdict(int) # default = 0 for e in edges: for f in e.flows: flow = { "arc": str(e.arc), "src_node": e.source.location, "dst_node": e.dest.location, "flow_id": f.id } result["placement"]["flows"].append(flow) for path in e.paths: # record edge delay: all flows take the same (shortest) path => take path delay path_delay = { "src": e.arc.source.name, "dest": e.arc.dest.name, "src_node": e.source.location, "dest_node": e.dest.location, "path_delay": sp.path_delay(links, path) } result["metrics"]["path_delays"].append(path_delay) result["metrics"]["total_path_delay"] += sp.path_delay(links, path) result["metrics"]["total_delay"] += sp.path_delay(links, path) # go through nodes of each path and increase the dr of the traversed links for i in range(len(path) - 1): # skip connections on the same node (no link used) if path[i] != path[i + 1]: consumed_dr[(path[i], path[i + 1])] += e.flow_dr() / len(e.paths) link = { "arc": str(e.arc), "edge_src": e.source.location, "edge_dst": e.dest.location, "link_src": path[i], "link_dst": path[i + 1] } result["placement"]["links"].append(link) # record VNF delay for i in instances: vnf_delay = { "vnf": i.component.name, "vnf_delay": i.component.vnf_delay } result["metrics"]["vnf_delays"].append(vnf_delay) result["metrics"]["total_vnf_delay"] += i.component.vnf_delay # record total delay = link + vnf delay result["metrics"]["total_delay"] = result["metrics"][ "total_path_delay"] + result["metrics"]["total_vnf_delay"] # link capacity violations result["placement"]["dr_oversub"] = [] max_dr = 0 for l in links.ids: if links.dr[l] < consumed_dr[l]: result["placement"]["dr_oversub"].append({"link": l}) if consumed_dr[l] - links.dr[l] > max_dr: max_dr = consumed_dr[l] - links.dr[l] result["metrics"]["max_dr_oversub"] = max_dr return result
def objective_value(overlays, print_info=False): # check delay of each edge; if too high, return math.inf for infeasible/infinity edges = [e for ol in overlays.values() for e in ol.edges] for e in edges: for path in e.paths: if sp.path_delay(links, path) > e.arc.max_delay: # print("Embedding INFEASIBLE because delay of path of {} is too high".format(e)) logger.warning( "Embedding INFEASIBLE because delay of path of {} is too high" .format(e)) return math.inf # calculate changed instances (compared to previous instances) curr_instances = {i for ol in overlays.values() for i in ol.instances} changed = prev_instances ^ curr_instances # instances that are were added or removed # record max over-subscription of node capacities consumed_cpu, consumed_mem = consumed_node_resources(overlays) max_cpu_over, max_mem_over = 0, 0 for v in nodes.ids: if consumed_cpu[v] - nodes.cpu[v] > max_cpu_over: max_cpu_over = consumed_cpu[v] - nodes.cpu[v] if consumed_mem[v] - nodes.mem[v] > max_mem_over: max_mem_over = consumed_mem[v] - nodes.mem[v] # calculate data rate of each link and mark used links for each edge consumed_dr = defaultdict(int) # default = 0 link_used = {} edges = [e for ol in overlays.values() for e in ol.edges] for e in edges: for path in e.paths: # go along nodes of the path and increment data rate of each traversed link for i in range(len(path) - 1): # skip connections on same node without a link (both inst at same node) if path[i] != path[i + 1]: # assume the edge dr is split equally among all paths (currently only 1 path per edge) consumed_dr[(path[i], path[i + 1])] += e.flow_dr() / len(e.paths) link_used[(e.arc, e.source.location, e.dest.location, path[i], path[i + 1])] = 1 # record max over-subscription of link capacitiy max_dr_over = 0 for l in links.ids: if consumed_dr[l] - links.dr[l] > max_dr_over: max_dr_over = consumed_dr[l] - links.dr[l] # calculate total delay over all used links (by different edges) total_delay = 0 for key in link_used: total_delay += links.delay[(key[3], key[4])] # calculate total vnf delay of each node and add it to total_delay vnf_delays = 0 for i in curr_instances: vnf_delays += i.component.vnf_delay # adding vnf_delay to total_delay total_delay += vnf_delays # calculate total consumed resources total_consumed_cpu = sum(consumed_cpu[v] for v in nodes.ids) total_consumed_mem = sum(consumed_mem[v] for v in nodes.ids) total_consumed_dr = sum(consumed_dr[l] for l in links.ids) # print objective value info if print_info: # print("Max over-subscription: {} (cpu), {} (mem), {} (dr)".format(max_cpu_over, max_mem_over, max_dr_over)) # print("Total delay: {}, Num changed instances: {}".format(total_delay, len(changed))) # print("Total consumed resources: {} (cpu), {} (mem), {} (dr)".format(total_consumed_cpu, total_consumed_mem, # total_consumed_dr)) logger.info( "Max over-subscription: {} (cpu), {} (mem), {} (dr)".format( max_cpu_over, max_mem_over, max_dr_over)) logger.info("Total delay: {}, Num changed instances: {}".format( total_delay, len(changed))) logger.info( "Total consumed resources: {} (cpu), {} (mem), {} (dr)".format( total_consumed_cpu, total_consumed_mem, total_consumed_dr)) # calculate objective value; objectives & weights have to be identical to the MIP # lexicographical combination of all objectives if obj == objective.COMBINED: w1 = 100 * 1000 * 1000 # assuming changed instances < 100 w2 = 1000 * 1000 # assuming total resource consumption < 1000 w3 = 1000 # assuming total delay < 1000 value = w1 * (max_cpu_over + max_mem_over + max_dr_over) value += w2 * len(changed) value += w3 * (total_consumed_cpu + total_consumed_mem + total_consumed_dr) value += total_delay # minimize max over-subscription elif obj == objective.OVER_SUB: value = max_cpu_over + max_mem_over + max_dr_over # minimize changed instances (compared to previous embedding) elif obj == objective.CHANGED: value = len(changed) # minimize total resource consumption elif obj == objective.RESOURCES: value = total_consumed_cpu + total_consumed_mem + total_consumed_dr # minimize total delay elif obj == objective.DELAY: value = total_delay else: logger.error("Objective {} unknown".format(obj)) raise ValueError("Objective {} unknown".format(obj)) return value
def format_output(result, changed_instances, overlays, nodes, links): instances, edges = set(), set() for ol in overlays: instances.update(ol.instances) edges.update(ol.edges) # save placement result['placement'] = {'vnfs': [], 'vlinks': []} result['metrics'] = {} for i in instances: vnf = {'name': i.component.name, 'node': i.location} result['placement']['vnfs'].append(vnf) result['metrics']['num_instances'] = len(result['placement']['vnfs']) for e in edges: vlink = {'src_vnf': e.source.component.name, 'src_node': e.source.location, 'dest_vnf': e.dest.component.name, 'dest_node': e.dest.location} result['placement']['vlinks'].append(vlink) # node capacity violations result['placement']['cpu_oversub'] = [] result['placement']['mem_oversub'] = [] max_cpu, max_mem = 0, 0 for v in nodes.ids: over_cpu = sum(i.consumed_cpu() for i in instances if i.location == v) - nodes.cpu[v] if over_cpu > 0: result['placement']['cpu_oversub'].append({'node': v}) if over_cpu > max_cpu: max_cpu = over_cpu over_mem = sum(i.consumed_mem() for i in instances if i.location == v) - nodes.mem[v] if over_mem > 0: result['placement']['mem_oversub'].append({'node': v}) if over_mem > max_mem: max_mem = over_mem result['metrics']['max_cpu_oversub'] = max_cpu result['metrics']['max_mem_oversub'] = max_mem # consumed node resources result['placement']['alloc_node_res'] = [] for i in instances: resources = {'name': i.component.name, 'node': i.location, 'cpu': i.consumed_cpu(), 'mem': i.consumed_mem()} result['placement']['alloc_node_res'].append(resources) # changed instances (compared to previous embedding) result['metrics']['changed'] = [] for i in changed_instances: result['metrics']['changed'].append({'name': i.component.name, 'node': i.location}) result['metrics']['num_changed'] = len(result['metrics']['changed']) # edge and link data rate, used links result['placement']['flows'] = [] result['metrics']['path_delays'] = [] result['metrics']['vnf_delays'] = [] result['metrics']['total_path_delay'] = 0 result['metrics']['total_vnf_delay'] = 0 result['metrics']['max_endToEnd_delay'] = 0 result['metrics']['total_delay'] = 0 result['placement']['links'] = [] consumed_dr = defaultdict(int) # default = 0 for e in edges: for f in e.flows: flow = {'arc': str(e.arc), 'src_node': e.source.location, 'dst_node': e.dest.location, 'src_vnf': e.source.component.name, 'dest_vnf': e.dest.component.name, 'flow_id': f.id} result['placement']['flows'].append(flow) for path in e.paths: # record edge delay: all flows take the same (shortest) path => take path delay path_delay = {'src': e.arc.source.name, 'dest': e.arc.dest.name, 'src_node': e.source.location, 'dest_node': e.dest.location, 'path_delay': sp.path_delay(links, path)} result['metrics']['path_delays'].append(path_delay) result['metrics']['total_path_delay'] += sp.path_delay(links, path) result['metrics']['total_delay'] += sp.path_delay(links, path) # go through nodes of each path and increase the dr of the traversed links for i in range(len(path) - 1): # skip connections on the same node (no link used) if path[i] != path[i+1]: consumed_dr[(path[i], path[i+1])] += e.flow_dr() / len(e.paths) link = {'arc': str(e.arc), 'edge_src': e.source.location, 'edge_dst': e.dest.location, 'link_src': path[i], 'link_dst': path[i+1]} result['placement']['links'].append(link) # record VNF delay for i in instances: vnf_delay = {'vnf': i.component.name, 'vnf_delay': i.component.vnf_delay} result['metrics']['vnf_delays'].append(vnf_delay) result['metrics']['total_vnf_delay'] += i.component.vnf_delay # record total delay = link + vnf delay result['metrics']['total_delay'] = result['metrics']['total_path_delay'] + result['metrics']['total_vnf_delay'] #record max end-to-end delay endToEnd = save_end2end_delay(edges,links) if endToEnd: result['metrics']['max_endToEnd_delay'] = max(endToEnd.values()) # for an empty placement, there is no end to end delay else: result['metrics']['max_endToEnd_delay'] = 0 # link capacity violations result['placement']['dr_oversub'] = [] max_dr = 0 for l in links.ids: if links.dr[l] < consumed_dr[l]: result['placement']['dr_oversub'].append({'link': l}) if consumed_dr[l] - links.dr[l] > max_dr: max_dr = consumed_dr[l] - links.dr[l] result['metrics']['max_dr_oversub'] = max_dr result['id'] = str(uuid.uuid4()) store.store_placement_result(result) return result['id']