summaryrefslogtreecommitdiff
path: root/esds/helpers/platform.py
blob: 3f50aa958edc829cbae88cb14c4fc710fbed9ff7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

import yaml, os
import numpy as np

class YAMLPlatformFile:

    def __init__(self, file_path):
        self.file_path=file_path
        self.default={
            "breakpoints": [],
            "breakpoints_every": None,
            "debug": False,
            "interferences": True,
            "node_count": 0,
            "implementations": [],
            "interfaces": dict()
        }

        with open(file_path) as f:
            self.platform = yaml.load(f, Loader=yaml.FullLoader)

        ##### General
        if "general" in self.platform:
            self.parse_general()
        ##### Nodes
        if "nodes" in self.platform:
            self.parse_nodes()
        else:
            self.parse_error("platform file has no nodes section")
        ##### Interfaces
        if "interfaces" in self.platform:
            self.parse_interfaces()
        else:
            self.parse_error("platform file has no interfaces section")

    def parse_error(self,msg):
        raise Exception("Fail to parse platform file \""+self.file_path+"\": "+msg)

    def parse_link_range(self,r):
        elt=r.split("-")
        if len(elt) == 2:
            return(range(int(elt[0]),int(elt[1])))
        else:
            return([int(elt[0])])

    def parse_bw(self,bw):
        for i,c in enumerate(bw):
            if not c.isdigit() and c != ".":
                break
        number=float(bw[:i])
        unit=bw[i:]
        number=number*1000 if unit == "Mbps" else number
        number=number*1000*8 if unit == "MBps" else number
        number=number*100 if unit == "kbps" else number
        number=number*100*8 if unit == "kBps" else number
        number=number*8 if unit == "Bps" else number
        return(number)

    def parse_lat(self,lat):
        for i,c in enumerate(lat):
            if not c.isdigit() and c != ".":
                break
        number=float(lat[:i])
        unit=lat[i:]
        number=number*60 if unit in ["m","M"] else number
        number=number*3600 if unit in ["h","H"] else number
        number=number/1000 if unit in ["ms","MS"] else number
        return(number)

    def parse_link(self,link):
        words=link.split()
        if len(words) == 4:
            return((
                self.parse_link_range(words[0]),
                self.parse_bw(words[1]),
                self.parse_lat(words[2]),
                self.parse_link_range(words[3])))
        elif len(words) == 2:
            return((
                range(0,self.default["node_count"]),
                self.parse_bw(words[0]),
                self.parse_lat(words[1]),
                range(0,self.default["node_count"])))
        return(None)

    def parse_txperf(self,txperf):
        elts=txperf.split()
        return((self.parse_bw(elts[0]),self.parse_lat(elts[1])))

    def parse_interfaces(self):
        interfaces=self.platform["interfaces"]
        node_count=self.default["node_count"]
        for i in interfaces:
            is_wired=not interfaces[i]["wireless"]
            links=list()
            if type(interfaces[i]["links"]) == list:
                for link in interfaces[i]["links"]:
                    links.append(self.parse_link(link))
            else:
                links.append(self.parse_link(interfaces[i]["links"]))
            ##### Create network matrix
            BW=np.full((node_count,node_count),0)
            LAT=np.full((node_count,node_count),0)
            for link in links:
                for n1 in link[0]:
                    for n2 in link[3]:
                        BW[n1][n2]=link[1]
                        LAT[n1][n2]=link[2]

            ##### Set txperfs for wireless interfaces
            if not is_wired:
                txperfs=interfaces[i]["txperfs"]
                for node in range(0,node_count):
                    txperf=txperfs[0] if len(txperfs) == 1 else txperfs[node]
                    bw,lat=self.parse_txperf(txperf)
                    BW[node][node]=bw
                    LAT[node][node]=lat

            self.default["interfaces"][i]={
                "is_wired": is_wired,
                "bandwidth": BW,
                "latency": LAT
            }

    def parse_nodes(self):
        nodes=self.platform["nodes"]
        if "count" in nodes:
            if not str(nodes["count"]).isnumeric():
                self.parse_error("node count should be a number")
            self.default["node_count"]=nodes["count"]
        if "implementations" in nodes:
            if type(nodes["implementations"]) != list:
                self.parse_error("nodes implementations should be a list of file path")
            for file in nodes["implementations"]:
                if not os.path.exists(file):
                    self.parse_error("File "+file+ " not found")
                path, extension = os.path.splitext(file)
                if extension != ".py":
                    self.parse_error("File "+file+" must be a python file")
                self.default["implementations"].append(path)
            count = len(nodes["implementations"])
            if count > 1 and count != self.default["node_count"]:
                self.parse_error("If more than one implementation is specified, each node implementation should be provided ("+str(self.default["node_count"])+" in total)")
            

    def parse_general(self):
        general=self.platform["general"]
        if "breakpoints" in general:
            if type(general["breakpoints"]) != list:
                self.parse_error("breakpoints should be a list of number")
            self.default["breakpoints"]=general["breakpoints"]
        if "breakpoints_every" in general:
            if not str(general["breakpoints_every"]).isnumeric():
                self.parse_error("breakpoints_every should be a number")
            self.default["breakpoints_every"]=general["breakpoints_every"]
        if "debug" in general:
            if type(general["debug"]) != bool:
                self.parse_error("debug should be on or off")
            self.default["debug"]=general["debug"]
        if "interferences" in general:
            if type(general["interferences"]) != bool:
                self.parse_error("interferences should be on or off")
            self.default["interferences"]=general["interferences"]