示例#1
0
def read_prev_placement(networkx, templates):
    # create empty overlays for all templates
    prev_embedding = {}  # dict: template -> overlay
    for t in templates:
        prev_embedding[t] = Overlay(t, [], [])

    # only read and recreate placement (not edges or flows)
    for v in networkx.nodes.data():
        node_id = v[0]
        node_attr = v[1]
        for vnf in node_attr['available_sf']:
            # find component that matches the VNF name (in any of the templates)
            for t in templates:
                # use first matching component (assuming it's only in one template); here, "vnf" is the vnf's name
                component = get_component(t, vnf)
                if component is not None:
                    # add new instance to overlay of corresponding template (source components need src_flows being set)
                    if component.source:
                        prev_embedding[t].instances.append(
                            Instance(component, node_id, src_flows=[]))
                    else:
                        prev_embedding[t].instances.append(
                            Instance(component, node_id))
                    break

    return prev_embedding
示例#2
0
def read_prev_embedding(file, templates, nodes, links):
    # create shortest paths
    shortest_paths = sp.all_pairs_shortest_paths(nodes, links)
    # create empty overlays for all templates
    prev_embedding = {}  # dict: template -> overlay
    for t in templates:
        prev_embedding[t] = Overlay(t, [], [])

    with open(file, "r") as f:
        yaml_file = yaml.load(f, yaml.SafeLoader)

        # read and create VNF instances of previous embedding
        for vnf in yaml_file["placement"]["vnfs"]:
            # find component that matches the VNF name (in any of the templates)
            for t in templates:
                # use first matching component (assuming it's only in one template)
                if vnf["name"] in [c.name for c in t.components]:
                    component = list(filter(lambda x: x.name == vnf["name"], t.components))[0]
                    # add new instance to overlay of corresponding template (source components need src_flows being set)
                    if component.source:
                        prev_embedding[t].instances.append(Instance(component, vnf["node"], src_flows=[]))
                    else:
                        prev_embedding[t].instances.append(Instance(component, vnf["node"]))
                    break

        # TODO: read and create flows. otherwise, adding edges really doesn't make a difference in the heuristic
        # read and create edges of previous embedding
        for edge in yaml_file["placement"]["vlinks"]:
            instances = [i for ol in prev_embedding.values() for i in ol.instances]

            # try to get source and dest instance from list of instances
            try:
                source = list(filter(lambda x: x.component.name == edge["src_vnf"] and x.location == edge["src_node"],
                                     instances))[0]
                dest = list(filter(lambda x: x.component.name == edge["dest_vnf"] and x.location == edge["dest_node"],
                                   instances))[0]
            # if the vnfs don't exist in prev_embedding (eg, through incorrect input), ignore the edge
            except IndexError:
                # print("No matching VNFs in prev_embedding for edge from {} to {}.
                #                                         Ignoring the edge.".format(source, dest))
                continue  # skip and continue with next edge

            # get arc from templates by matching against source and dest components
            for t in templates:
                if source.component in t.components and dest.component in t.components:
                    # assume t has an arc source->dest if both components are in t
                    arc = list(filter(lambda x: x.source == source.component and x.dest == dest.component, t.arcs))[0]
                    # add new edge to overlay of corresponding template
                    edge = Edge(arc, source, dest)
                    prev_embedding[t].edges.append(edge)
                    edge.paths.append(shortest_paths[(source.location, dest.location)][0])

    return prev_embedding
示例#3
0
def read_result(file, components, arcs):
    reading_instances, reading_edges = False, False
    instances, edges = [], []

    with open(file, "r") as sources_file:
        reader = csv.reader((row for row in sources_file), delimiter="\t")
        for row in reader:
            # empty line always means end of a segment
            if len(row) == 0:
                reading_instances = False
                reading_edges = False
                continue

            # start reading instances
            if row[0].startswith("# instances:"):
                reading_instances = True
            elif row[0].startswith("# edges:"):
                reading_edges = True

            # read instances: only set relevant attributes
            if reading_instances and len(row) == 2:
                component = list(filter(lambda x: x.name == row[0],
                                        components))[0]
                src_flows = None
                if component.source:
                    src_flows = []
                instances.append(Instance(component, row[1], src_flows))

            # read edges: only set relevant attributes + mapped flows
            if reading_edges and len(row) == 4:
                arc = list(filter(lambda x: str(x) == row[0], arcs))[0]
                # store tuples of (arc, start_node, end_node, flow_id)
                edges.append((arc, row[1], row[2], row[3]))

    return instances, edges
示例#4
0
def map_flow2edge(overlay, start_instance, arc, flow, flow_dr, tabu):
    # determine if the instances of the destination component are fixed => if so, cannot place new instances
    fixed = False
    for i in overlay.instances:
        if i.component == arc.dest and i.fixed:
            fixed = True
            break
    best_node = find_best_node(overlay, start_instance.location, arc, flow_dr,
                               fixed, tabu)
    if best_node is None:
        logger.error(f"No suitable node found. Cannot compute placement.")
        return False

    # if the instance at best node already exists (e.g., from forward dir), just connect to it, else create anew
    # look for existing instance
    instance_exists = False
    for i in overlay.instances:
        if i.component == arc.dest and i.location == best_node:
            instance_exists = True
            dest_instance = i
            break
    # create new instance if none exists in the overlay
    if not instance_exists:
        dest_instance = Instance(arc.dest, best_node)
        overlay.instances.append(dest_instance)
        # print("\tAdded new instance {} at best node {} (may exist in other overlays)".format(dest_instance,
        #        best_node))
        logger.info(
            "\tAdded new instance {} at best node {} (may exist in other overlays)"
            .format(dest_instance, best_node))

    # check if edge to dest_instance already exists
    edge_exists = False
    if instance_exists:
        if dest_instance in start_instance.edges_out.keys():
            edge_exists = True
            edge = start_instance.edges_out[dest_instance]

    # if it doesn't exist, create a new edge and assign a path (shortest path)
    if not edge_exists:
        edge = Edge(arc, start_instance, dest_instance)
        overlay.edges.append(edge)
        edge.paths.append(shortest_paths[(start_instance.location,
                                          dest_instance.location)][0])

    # map flow to edge
    flow.dr[edge] = flow_dr
    edge.flows.append(flow)
    # print("\tMapped flow {} (dr {}) to edge {} (new: {})".format(flow, flow_dr, edge, not edge_exists))
    logger.info("\tMapped flow {} (dr {}) to edge {} (new: {})".format(
        flow, flow_dr, edge, not edge_exists))
    return True
示例#5
0
    def __deepcopy__(self, memodict={}):
        new_overlay = Overlay(self.template, [], [])  # empty overlay
        instance_dict = {
        }  # dict of old to new instances; for easy access when adding the edges
        flow_dict = {}  # same for old to new flows

        # add new instances with same attributes (component, etc) but without edges_in/out
        for i in self.instances:
            # copy src_flows
            new_src_flows = None
            if i.src_flows:
                new_src_flows = []
                for f in i.src_flows:
                    new_flow = Flow(f.id, f.src_dr)
                    flow_dict[f] = new_flow
                    new_src_flows.append(new_flow)
            # copy instances
            new_instance = Instance(i.component, i.location, new_src_flows,
                                    i.fixed)
            new_overlay.instances.append(new_instance)
            instance_dict[i] = new_instance

        # add new edges in topological order => sets edges_in/out etc automatically
        for e in self.topological_order(True):
            # source/dest from new_overlay's instances using the instance_dict
            new_source = instance_dict[e.source]
            new_dest = instance_dict[e.dest]
            # create new edge with references to the new instances and manually set the remaining attributes
            new_edge = Edge(e.arc, new_source, new_dest)
            new_edge.direction = e.direction
            new_edge.paths = copy.deepcopy(
                e.paths, memodict)  # deepcopy for copying list of lists

            # copy and update flows
            for f in e.flows:
                new_flow = flow_dict[f]
                new_edge.flows.append(new_flow)
                new_flow.dr[new_edge] = f.dr[e]
                if new_edge.source.component.stateful:
                    new_flow.passed_stateful[
                        new_edge.source.component] = new_edge.source
                elif new_edge.dest.component.stateful:
                    new_flow.passed_stateful[
                        new_edge.dest.component] = new_edge.dest

            new_overlay.edges.append(new_edge)

        return new_overlay
示例#6
0
def solve(arg_nodes,
          arg_links,
          templates,
          prev_overlays,
          sources,
          fixed,
          arg_shortest_paths,
          tabu=set()):
    # print("Previous overlays:")
    # for ol in prev_overlays.values():
    #     ol.print()
    # tabu_string = ""
    # for i in tabu:
    #     tabu_string += "({},{}) ".format(i[0], i[1])
    #     print("Tabu list: {}".format(tabu_string))

    # write global variables
    global nodes, links, shortest_paths, overlays
    nodes = arg_nodes
    links = arg_links
    shortest_paths = arg_shortest_paths

    # keep previous overlays of templates that still exist
    overlays = {t: ol for t, ol in prev_overlays.items() if t in templates}

    # create empty overlays for new templates
    for t in templates:
        if t not in overlays.keys():
            overlays[t] = Overlay(t, [], [])
            # print("Created empty overlay for new template {}".format(t))
            logger.info("Created empty overlay for new template {}".format(t))

    # remove all instances of fixed components => curr fixed instances added again later; prev fixed instances removed
    fixed_components = {f.component for f in fixed}
    fixed_instances = {
        i
        for ol in overlays.values() for i in ol.instances
        if i.component in fixed_components
    }
    # print("Remove any existing fixed instances:", *fixed_instances, sep=" ")
    for i in fixed_instances:
        remove_instance(i)

    # embed templates sequentially in given order
    for t in templates:
        # print("\n-Embedding template: {}-".format(t))
        logger.info("-Embedding template: {}-".format(t))

        own_sources = [src for src in sources if src.component in t.components]
        update_sources(overlays[t], own_sources)

        # add fixed instances that match template t's components
        for f in fixed:
            if f.component in t.components:
                fixed_instance = Instance(f.component, f.location, fixed=True)
                if fixed_instance not in overlays[t].instances:
                    overlays[t].instances.append(fixed_instance)
                    # print("Added fixed instance of {} at {}".format(f.component, f.location))
                    logger.info("Added fixed instance of {} at {}".format(
                        f.component, f.location))

        # iterate over all instances in topological order; start in forward direction then switch to backward
        i = 0
        direction = "forward"
        while i < len(overlays[t].topological_order()):
            instance = overlays[t].topological_order()[i]
            # #print("Topological order:", *overlays[t].topological_order(), sep=" ")

            # remove unused instances (except fixed instances)
            if not instance.fixed:
                if not instance.used(direction, overlays[t]):
                    # print("Removed unused instance {} from overlay of {}".format(instance, t))
                    logger.info(
                        "Removed unused instance {} from overlay of {}".format(
                            instance, t))
                    remove_instance(instance, overlays[t])
                    continue

            # switch direction at the first instance of an end component (bc outgoing not ingoing direction considered)
            if instance.component.end:
                direction = "backward"

            # get outgoing flows (and their dr) for each output
            out_flows = instance.out_flows(direction)
            for k in range(len(out_flows)):
                arc = out_arc(t, instance.component, k, direction)
                # when a component is adapted for reuse, it has separate outputs for the arcs of different templates
                if arc is None:  # for output k, this template has no arc => skip to next output
                    # print("{}'s outgoing arc at output {} in {} direction belongs to a different template.
                    #        The output is skipped".format(instance, k, direction))
                    logger.debug(
                        "{}'s outgoing arc at output {} in {} direction belongs to a different template. "
                        "The output is skipped".format(instance, k, direction))
                    continue

                update_flow_mapping(overlays[t], instance, arc, out_flows[k],
                                    tabu)
                # print("Updated the flow mapping along arc {} at {}\n".format(arc, instance))
                logger.info(
                    "Updated the flow mapping along arc {} at {}\n".format(
                        arc, instance))

            i += 1

        # print()
        if overlays[t].empty():
            del overlays[t]
            # print("Deleted empty overlay of {}".format(t))
            logger.info("Deleted empty overlay of {}".format(t))
    # else:
    # overlays[t].print()
    # print("Topological order:", *overlays[t].topological_order(), sep=" ")
    # print()

    return overlays
示例#7
0
def update_sources(overlay, sources):
    # reset passed_stateful for all flows (set up to date later) and remove outdated flows
    # print("Reset passed_stateful for all flows of template {}".format(overlay.template))
    src_flows = {f for src in sources for f in src.flows}
    mapped_flows = {f
                    for e in overlay.edges for f in e.flows
                    } | {f
                         for src in sources for f in src.flows}
    for f in mapped_flows:
        f.passed_stateful.clear()
        if f not in src_flows:
            remove_flow(overlay, f)

    # add/update source instances
    for src in sources:
        # get existing source instance at the location
        src_exists = False
        for i in overlay.instances:
            if i.component == src.component and i.location == src.location:
                src_exists = True
                break

        # update or add source instance depending on whether such an instance already exists or not
        if src_exists:
            # remove outdated flows
            for f in i.src_flows:
                if f not in src.flows:
                    i.src_flows.remove(f)
                    for e in f.dr:
                        e.flows.remove(f)
                    f.dr.clear()
                    f.passed_stateful.clear()

            # update or add new flows
            for f in src.flows:
                # if the flow already exists, keep the existing flow and only update its src_dr
                if f in i.src_flows:
                    new_src_dr = f.src_dr
                    f = i.src_flows[i.src_flows.index(
                        f)]  # get existing flow object in i.src_flows
                    f.src_dr = new_src_dr
                # else add the new flow
                else:
                    i.src_flows.append(f)
                f.passed_stateful[i.component] = i
            # print("Updated/checked src_flows of existing source instance {}".format(i))
            logger.info(
                "Updated/checked src_flows of existing source instance {}".
                format(i))
        else:
            src_instance = Instance(src.component, src.location, src.flows)
            overlay.instances.append(src_instance)
            # print("Added new source instance {}".format(src_instance))
            logger.info("Added new source instance {}".format(src_instance))

    # remove old source instances without source
    source_instances = [i for i in overlay.instances if i.component.source]
    for src in source_instances:
        corresponding_sources = {
            s
            for s in sources
            if s.component == src.component and s.location == src.location
        }
        if len(corresponding_sources) == 0:
            # print("Remove source instance {} without corresponding source".format(src))
            logger.info(
                "Remove source instance {} without corresponding source".
                format(src))
            remove_instance(src)
示例#8
0
def solve(arg_nodes, arg_links, templates, prev_overlays, sources, fixed,
          arg_obj):
    # write global variables
    global nodes, links, prev_instances, obj
    nodes = arg_nodes
    links = arg_links
    # copy previous instances (attributes like edges_in etc are not needed and not copied)
    prev_instances = {
        Instance(i.component, i.location, i.src_flows)
        for ol in prev_overlays.values() for i in ol.instances
    }
    obj = arg_obj

    # print input
    # print("Templates:", *templates, sep=" ")
    # print("Sources:", *sources, sep=" ")
    # print("Fixed instances:", *fixed, sep=" ")
    # print("Previous instances:", *prev_instances, sep=" ")

    # pre-computation of shortest paths
    start_init = time.time()
    shortest_paths = sp.all_pairs_shortest_paths(nodes, links)
    init_time = time.time() - start_init
    # print("Time for pre-computation of shortest paths: {}s\n".format(init_time))
    logger.info(
        "Time for pre-computation of shortest paths: {}s\n".format(init_time))

    start_heuristic = time.time()
    # get total source data rate for each source component (for sorting the templates later)
    src_drs = defaultdict(int)  # default = 0
    for src in sources:
        src_drs[src.component] += src.total_flow_dr()

    # sort templates with decreasing weight: heaviest/most difficult templates get embedded first
    templates.sort(key=lambda t: t.weight(src_drs[t.source()]), reverse=True)
    # print("Templates sorted to start with heaviest:", *templates, sep=" ")

    # initial solution
    # print("\n----- Initial solution -----")
    logger.info("----- Initial solution -----")
    overlays = heuristic.solve(arg_nodes, arg_links, templates, prev_overlays,
                               sources, fixed, shortest_paths)
    obj_value = objective_value(overlays)
    # print("Objective value of initial solution: {}".format(obj_value))
    # print("Runtime for initial solution: {}".format(time.time() - start_heuristic))
    logger.info("Objective value of initial solution: {}".format(obj_value))
    logger.info("Runtime for initial solution: {}\n".format(time.time() -
                                                            start_heuristic))

    # iterative improvement
    if len(nodes.ids) > 1:  # doesn't work for networks with just 1 node
        # print("\n----- Iterative improvement -----")
        logger.info("----- Iterative improvement -----")
        overlays = improvement.improve(arg_nodes, arg_links, templates,
                                       overlays, sources, fixed,
                                       shortest_paths)
        obj_value = objective_value(overlays)
        runtime = time.time() - start_heuristic
        # print("Objective value after improvement: {}".format(obj_value))
        # print("Heuristic runtime: {}s".format(runtime))
        logger.info("Objective value after improvement: {}".format(obj_value))
        logger.info("Heuristic runtime: {}s".format(runtime))
    else:
        runtime = time.time() - start_heuristic
        # print("Skip iterative improvement for network with just 1 node")
        logger.info("Skip iterative improvement for network with just 1 node")

    # calculate changed instances for writing result
    curr_instances = {i for ol in overlays.values() for i in ol.instances}
    changed = prev_instances ^ curr_instances  # instances that were added or removed

    return init_time, runtime, obj_value, changed, overlays