Compare commits
1702
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ce07884f4 | ||
|
|
be53ac31d6 | ||
|
|
50bfbd0566 | ||
|
|
a4d8049661 | ||
|
|
da4c53b3c9 | ||
|
|
1b96ff3539 | ||
|
|
93d04123c6 | ||
|
|
22655cf9fd | ||
|
|
fe82d2c04a | ||
|
|
0534932bf3 | ||
|
|
5f68be2273 | ||
|
|
68ae67dc58 | ||
|
|
c4a85ab1d1 | ||
|
|
ef3c885630 | ||
|
|
bb35d04102 | ||
|
|
ba05fd363b | ||
|
|
e988b0f660 | ||
|
|
b27cce08af | ||
|
|
a38896c254 | ||
|
|
63ae13fcf9 | ||
|
|
c5f6d0ff17 | ||
|
|
ab8c4e9198 | ||
|
|
8daa4193c7 | ||
|
|
5c895eaad8 | ||
|
|
435d0d68b3 | ||
|
|
66e9845f05 | ||
|
|
fe3340077c | ||
|
|
c589660e17 | ||
|
|
d9117c77e1 | ||
|
|
6cb68f26c9 | ||
|
|
c60764c34f | ||
|
|
ba723826b7 | ||
|
|
0165d7bdff | ||
|
|
1df1f9a1c5 | ||
|
|
2c916acc06 | ||
|
|
0a810b226c | ||
|
|
57c8f5934f | ||
|
|
125e669723 | ||
|
|
a2486a6d66 | ||
|
|
15f2898f26 | ||
|
|
1c4adde1b6 | ||
|
|
4d22991245 | ||
|
|
90ea6c6bd1 | ||
|
|
269cecd3ee | ||
|
|
c2be20f09e | ||
|
|
df56fa2716 | ||
|
|
f49e2539b7 | ||
|
|
4da7b3b7f0 | ||
|
|
e5a5cd0070 | ||
|
|
c5779deeb6 | ||
|
|
79c944aef4 | ||
|
|
a23c6a2871 | ||
|
|
7acc693277 | ||
|
|
889698f7bd | ||
|
|
f6f6d9282b | ||
|
|
5224884e5e | ||
|
|
f063b023bd | ||
|
|
a1edb167b1 | ||
|
|
613fac4bab | ||
|
|
e0cd11ab21 | ||
|
|
5b818ad75c | ||
|
|
17ba6a7c4d | ||
|
|
f77a8b9e17 | ||
|
|
3dd8c2cda6 | ||
|
|
48a8ed857e | ||
|
|
74c3511d5c | ||
|
|
5ac113cfaa | ||
|
|
cd02954c91 | ||
|
|
93be1d0401 | ||
|
|
6a38811f1a | ||
|
|
4ac45a3967 | ||
|
|
d7e01bb821 | ||
|
|
340bf3cb7a | ||
|
|
5f8547a5e0 | ||
|
|
09e9c0550a | ||
|
|
c1dc80571a | ||
|
|
ec9b23ba9c | ||
|
|
7c0b56f9ba | ||
|
|
d90a45514f | ||
|
|
b2fe0778dc | ||
|
|
3e4f254573 | ||
|
|
2e4dd13482 | ||
|
|
19331ca287 | ||
|
|
9483f6378f | ||
|
|
a4e933688a | ||
|
|
c11e9f3316 | ||
|
|
acdcf9b6ae | ||
|
|
c702840a0a | ||
|
|
254fae2dc9 | ||
|
|
bdad3e8c8e | ||
|
|
574d634fc2 | ||
|
|
eafbd2a597 | ||
|
|
7485b035af | ||
|
|
8014a7023c | ||
|
|
bec34e7227 | ||
|
|
9ca6322603 | ||
|
|
957fe1db93 | ||
|
|
3b4738c2ca | ||
|
|
30ebb852a3 | ||
|
|
959586bc8f | ||
|
|
9bda98486d | ||
|
|
c68e4e76f4 | ||
|
|
98a8231cef | ||
|
|
28ce13d18e | ||
|
|
ef59cd88dc | ||
|
|
9db6cdc261 | ||
|
|
f7b29333f5 | ||
|
|
24520fc982 | ||
|
|
b93cc989f4 | ||
|
|
a8cc243aa7 | ||
|
|
e65dc4c6d6 | ||
|
|
d4be92713a | ||
|
|
79f5d39db5 | ||
|
|
6da3fcdef1 | ||
|
|
026130bd5f | ||
|
|
c1b48fb5c8 | ||
|
|
b1983548aa | ||
|
|
c62812a944 | ||
|
|
d66b96260a | ||
|
|
d7e9c3bbfd | ||
|
|
7a4a865bdb | ||
|
|
da4dcf3753 | ||
|
|
5383b0e603 | ||
|
|
9d2e6404b3 | ||
|
|
91a73bd191 | ||
|
|
c069542c5c | ||
|
|
127f35a9bc | ||
|
|
171e815122 | ||
|
|
3ac5cafe53 | ||
|
|
a7ad739036 | ||
|
|
a84bec9709 | ||
|
|
05dbf82edb | ||
|
|
c75eb47158 | ||
|
|
9945864a32 | ||
|
|
beeea4b292 | ||
|
|
7a3159c9af | ||
|
|
ccd6b5cd7d | ||
|
|
908e85d9f9 | ||
|
|
e341526f46 | ||
|
|
03461ce908 | ||
|
|
8c37975d44 | ||
|
|
4d55ef1132 | ||
|
|
342d47cfb5 | ||
|
|
554abdd57a | ||
|
|
061598f011 | ||
|
|
d754a7ceea | ||
|
|
643c9f912f | ||
|
|
4965421215 | ||
|
|
18aab5b786 | ||
|
|
b905dddf19 | ||
|
|
11a9c46904 | ||
|
|
b1ec912db5 | ||
|
|
db45b7c644 | ||
|
|
2256a53830 | ||
|
|
03ef55672d | ||
|
|
c45bfab318 | ||
|
|
b2fcab827d | ||
|
|
1471da95a9 | ||
|
|
5a530af189 | ||
|
|
3c9f5ad56e | ||
|
|
c5d9e0e03c | ||
|
|
0094997aa1 | ||
|
|
7a411b6bb5 | ||
|
|
c2341cd47a | ||
|
|
4219b22978 | ||
|
|
08ab7e8de7 | ||
|
|
3ef6bcface | ||
|
|
ab97dfa19c | ||
|
|
af1ccf93a5 | ||
|
|
015cd9e776 | ||
|
|
3d44bcdcc4 | ||
|
|
1817cbc3a4 | ||
|
|
e0bf8ece3c | ||
|
|
37bdf14b49 | ||
|
|
292e69f89a | ||
|
|
dd876e69c8 | ||
|
|
4e644d7465 | ||
|
|
596fd9d0c6 | ||
|
|
474f68600e | ||
|
|
50774e407c | ||
|
|
8e63e2c540 | ||
|
|
273c4e9403 | ||
|
|
c273f49077 | ||
|
|
9a8bdfd765 | ||
|
|
23dfbe4ae2 | ||
|
|
b57e5d93b8 | ||
|
|
f0f9907b9d | ||
|
|
c4fc630413 | ||
|
|
aabbf29542 | ||
|
|
b7898befef | ||
|
|
6721f0655e | ||
|
|
d37d6ecd99 | ||
|
|
e4db698184 | ||
|
|
e661a4b1c3 | ||
|
|
f5164f86b7 | ||
|
|
9e0c1549f4 | ||
|
|
55b455e9ae | ||
|
|
34933ef741 | ||
|
|
bbf94cdb63 | ||
|
|
593fc365d0 | ||
|
|
1edf3515e3 | ||
|
|
0d271f9477 | ||
|
|
3534f77761 | ||
|
|
64bbaeccc4 | ||
|
|
de002b0d9a | ||
|
|
307302ed32 | ||
|
|
a60f77ad36 | ||
|
|
383c3fb3e4 | ||
|
|
a64f8c28bd | ||
|
|
377d1b1cd8 | ||
|
|
dde2263fe7 | ||
|
|
434619053a | ||
|
|
b739cd11c8 | ||
|
|
bc6ef0dad8 | ||
|
|
3ac0dd7093 | ||
|
|
0f0fe82350 | ||
|
|
6be4bfd026 | ||
|
|
d9d209f34d | ||
|
|
2059786f7b | ||
|
|
6008f987d3 | ||
|
|
635e8f11f3 | ||
|
|
9b5fa1d7ed | ||
|
|
1955e90493 | ||
|
|
222e87daeb | ||
|
|
a89ccb789e | ||
|
|
2013b7ae81 | ||
|
|
3e1100b71a | ||
|
|
086afe852b | ||
|
|
abcdf18547 | ||
|
|
4a304688f4 | ||
|
|
d3377bf911 | ||
|
|
481cbea10c | ||
|
|
b817cbc0ed | ||
|
|
15275048b6 | ||
|
|
3baccfee3a | ||
|
|
bb76c21275 | ||
|
|
c92c4efb66 | ||
|
|
39203484a6 | ||
|
|
2a2d605074 | ||
|
|
0676346aeb | ||
|
|
93fdb69ee3 | ||
|
|
c7921fe402 | ||
|
|
3660e6d02f | ||
|
|
083322a217 | ||
|
|
8225105cf2 | ||
|
|
ccd3276dcf | ||
|
|
f51eddef6d | ||
|
|
4f8aa1d8a0 | ||
|
|
060d46a282 | ||
|
|
820c3be7df | ||
|
|
a562ca1be3 | ||
|
|
658761d953 | ||
|
|
1b69ea13f5 | ||
|
|
6e5ac34bc2 | ||
|
|
ec9f02c7ab | ||
|
|
f75bee62b5 | ||
|
|
e77123cf0e | ||
|
|
8dea57d271 | ||
|
|
634761b8a7 | ||
|
|
01aac350fa | ||
|
|
08d611f49b | ||
|
|
f6de358b06 | ||
|
|
f66904ab90 | ||
|
|
6a363f65e3 | ||
|
|
736db20c9f | ||
|
|
ba8ea8f0cc | ||
|
|
eab4ea3fb9 | ||
|
|
06d9e4bba7 | ||
|
|
38ce329e7e | ||
|
|
94f088d7e8 | ||
|
|
89fb081983 | ||
|
|
aae0affae4 | ||
|
|
8190e81ba8 | ||
|
|
16a2a6dc55 | ||
|
|
b7999978f9 | ||
|
|
e1711f357a | ||
|
|
501eb4f916 | ||
|
|
f341159300 | ||
|
|
cac98f5fd5 | ||
|
|
5eca0802d2 | ||
|
|
6159e923e6 | ||
|
|
2ceb57915a | ||
|
|
61c144b9f6 | ||
|
|
8537e8d9fa | ||
|
|
33d42e25e6 | ||
|
|
7c5d2771f7 | ||
|
|
098bede0ec | ||
|
|
629e58ca5b | ||
|
|
e3ab3fc239 | ||
|
|
f1123e71ae | ||
|
|
778267aa75 | ||
|
|
d6da13d4e3 | ||
|
|
92a4f2ddbf | ||
|
|
ded25a9ef8 | ||
|
|
1bae1aab03 | ||
|
|
01a1e8e400 | ||
|
|
9487cb508d | ||
|
|
461751e5cd | ||
|
|
762479c15b | ||
|
|
c8607467bd | ||
|
|
f8e98d6a7f | ||
|
|
4aa0b82381 | ||
|
|
f9522b30dc | ||
|
|
ec26c7492d | ||
|
|
684092bae1 | ||
|
|
9cbf4688b2 | ||
|
|
74857ba474 | ||
|
|
f1b61c629a | ||
|
|
a565bdd57d | ||
|
|
cf5bbd597a | ||
|
|
c0d8fc0d37 | ||
|
|
eef43402b6 | ||
|
|
54bd9e6112 | ||
|
|
fa7edec7c8 | ||
|
|
89cc29b4c2 | ||
|
|
686a9993f5 | ||
|
|
e16c5fe905 | ||
|
|
dedb06b2f5 | ||
|
|
5fc3f57ede | ||
|
|
5789dae8be | ||
|
|
0efde9c4ed | ||
|
|
7eeaed992c | ||
|
|
f0ce6f501d | ||
|
|
823d1b9990 | ||
|
|
61760eabc5 | ||
|
|
12095a12f4 | ||
|
|
412726d39c | ||
|
|
136c959191 | ||
|
|
2ac4cd43da | ||
|
|
f603052b35 | ||
|
|
47be38e63c | ||
|
|
9ca775cba3 | ||
|
|
e4d49e6df7 | ||
|
|
02bf34aa96 | ||
|
|
73ef3e9a39 | ||
|
|
55ef99b667 | ||
|
|
57686d3149 | ||
|
|
8396960ec0 | ||
|
|
3e8df323b3 | ||
|
|
706229da77 | ||
|
|
2c10a8e687 | ||
|
|
e4c4891a47 | ||
|
|
80d647a9b0 | ||
|
|
c5e8f09b10 | ||
|
|
08643fe679 | ||
|
|
3df3610199 | ||
|
|
00cbb348a7 | ||
|
|
cde6c3aaf4 | ||
|
|
c265deedef | ||
|
|
0333d3dbf4 | ||
|
|
58324bdc50 | ||
|
|
04c1c098ed | ||
|
|
f2458d1dcf | ||
|
|
ce781a1832 | ||
|
|
b85943dc0a | ||
|
|
d4ca1db60b | ||
|
|
47d567e53c | ||
|
|
6b8d3538ef | ||
|
|
05f3fbae73 | ||
|
|
65e33fed9b | ||
|
|
d334c1ccfe | ||
|
|
fe8358add2 | ||
|
|
2c76ab718b | ||
|
|
aa4dfda44c | ||
|
|
66ae58de17 | ||
|
|
ce1673803f | ||
|
|
73adba8b81 | ||
|
|
c11d577349 | ||
|
|
be1ed10363 | ||
|
|
8a987b9c55 | ||
|
|
c75ff7ecd9 | ||
|
|
92075113d8 | ||
|
|
13bdd914dc | ||
|
|
60b0c7a914 | ||
|
|
212399feaf | ||
|
|
08d83d136d | ||
|
|
dd6424fee8 | ||
|
|
720a846cf8 | ||
|
|
bfdbb7089a | ||
|
|
16a384ab4b | ||
|
|
a3e1a9a44d | ||
|
|
6a69c2f699 | ||
|
|
db888fa5d4 | ||
|
|
c9b844a721 | ||
|
|
604ad455ee | ||
|
|
8d493b686e | ||
|
|
c0e7e34916 | ||
|
|
1a53141502 | ||
|
|
1285fb22dc | ||
|
|
4550fff1af | ||
|
|
7c4e5b14cb | ||
|
|
f72d3dfa32 | ||
|
|
e67539752e | ||
|
|
db0f36f431 | ||
|
|
7e9d3f2b50 | ||
|
|
4015e0666e | ||
|
|
cee62eb28e | ||
|
|
735080a84b | ||
|
|
161e7997fa | ||
|
|
417d79780f | ||
|
|
41a54f05cb | ||
|
|
84c1c24ce2 | ||
|
|
af4c9719b3 | ||
|
|
39a3b73f85 | ||
|
|
e8623fdc91 | ||
|
|
06dbd774a1 | ||
|
|
a280501ff1 | ||
|
|
891a9e8bdf | ||
|
|
7ae39fffc2 | ||
|
|
a56d9a6661 | ||
|
|
b0048c0aca | ||
|
|
0983ed29bb | ||
|
|
88763cfbe1 | ||
|
|
19dd7f70c1 | ||
|
|
42cdda38bb | ||
|
|
f0fd84770a | ||
|
|
a2d0ea78be | ||
|
|
e9d034bd31 | ||
|
|
a3d51b77e9 | ||
|
|
4b65110e4d | ||
|
|
eba13f0ca8 | ||
|
|
c1934706bb | ||
|
|
f67a7b64c8 | ||
|
|
628e84068e | ||
|
|
78a32e93fd | ||
|
|
859bfea502 | ||
|
|
2973798ca1 | ||
|
|
b9a15f071c | ||
|
|
f11dbe81d2 | ||
|
|
6bc9d68485 | ||
|
|
5cd6b553a5 | ||
|
|
5b13dc62ce | ||
|
|
4d381f0b58 | ||
|
|
84ce84f501 | ||
|
|
5b0897701b | ||
|
|
5465246245 | ||
|
|
e183e69997 | ||
|
|
779ea5f0ad | ||
|
|
00d1963484 | ||
|
|
5ac3cde2bd | ||
|
|
2a08dec648 | ||
|
|
b7268856d7 | ||
|
|
1b2c7a3193 | ||
|
|
b5962e8ee9 | ||
|
|
ece509d579 | ||
|
|
2935000485 | ||
|
|
708b184397 | ||
|
|
9a11544d87 | ||
|
|
2451d757bd | ||
|
|
ea97dea902 | ||
|
|
a19cc91537 | ||
|
|
796b281bf1 | ||
|
|
72fd120dc8 | ||
|
|
4794871a9a | ||
|
|
13d25b4109 | ||
|
|
21b50c962e | ||
|
|
54b29af818 | ||
|
|
8f34fa4c88 | ||
|
|
872e336462 | ||
|
|
b7a112cbec | ||
|
|
5a9c74be03 | ||
|
|
38addf2e24 | ||
|
|
3878c000fe | ||
|
|
1324ae6262 | ||
|
|
3a52ad2f53 | ||
|
|
c23c992f14 | ||
|
|
73f477be9d | ||
|
|
4797b42005 | ||
|
|
6845fd8339 | ||
|
|
8e2443ada4 | ||
|
|
84ea8d7f90 | ||
|
|
1aed2b8abd | ||
|
|
191df1cb73 | ||
|
|
b6be1e2810 | ||
|
|
a1acfa89c2 | ||
|
|
93ddd92662 | ||
|
|
3131c90344 | ||
|
|
3b484491ce | ||
|
|
3b24bd7abd | ||
|
|
e9013d761f | ||
|
|
c49b216c1f | ||
|
|
16ddf6560a | ||
|
|
0e733c7754 | ||
|
|
9c3ecfe338 | ||
|
|
771850b9cf | ||
|
|
355696f3dd | ||
|
|
342b743b13 | ||
|
|
40a4a25dd8 | ||
|
|
25979e715e | ||
|
|
82e0e9f38f | ||
|
|
549f1ebc8f | ||
|
|
00803bcd7f | ||
|
|
af2f67d98c | ||
|
|
752c2d6e7c | ||
|
|
3c3344e1f5 | ||
|
|
68e2e45f7d | ||
|
|
6a81b6dfb3 | ||
|
|
4e76439c79 | ||
|
|
0d39f11034 | ||
|
|
893cf61c21 | ||
|
|
ebc1eae679 | ||
|
|
29e5bee34e | ||
|
|
de41192ea7 | ||
|
|
b3055067ac | ||
|
|
9109233886 | ||
|
|
e49c9d2600 | ||
|
|
87b6021428 | ||
|
|
50f5080912 | ||
|
|
3641723193 | ||
|
|
8b215af818 | ||
|
|
586a9bb631 | ||
|
|
00c3238e50 | ||
|
|
e07775c7c3 | ||
|
|
4579b303e6 | ||
|
|
32d3c2bc79 | ||
|
|
ba43451bd6 | ||
|
|
f1e42ba5fa | ||
|
|
163a66c64c | ||
|
|
876e66e555 | ||
|
|
93cd5583eb | ||
|
|
5797f5852e | ||
|
|
15d2d76972 | ||
|
|
5cb4a5424d | ||
|
|
4d1a9cdc1d | ||
|
|
c3bf407adf | ||
|
|
14e14f1b87 | ||
|
|
bffe045267 | ||
|
|
92a28881b1 | ||
|
|
ebac6784d4 | ||
|
|
d9376439b1 | ||
|
|
d718bb72e9 | ||
|
|
40ea9172f9 | ||
|
|
787f8234ce | ||
|
|
cc20908bb3 | ||
|
|
a7eb557680 | ||
|
|
0fe73a6780 | ||
|
|
a905be2260 | ||
|
|
8f5f38c6a5 | ||
|
|
d82900d3a8 | ||
|
|
e62715870a | ||
|
|
2e19ceb0aa | ||
|
|
a0bc100289 | ||
|
|
5e60ee266b | ||
|
|
7c29c2ebf2 | ||
|
|
4b7b23cf20 | ||
|
|
2dee3413cf | ||
|
|
59cbec3fc9 | ||
|
|
3e2eb71316 | ||
|
|
06115a0456 | ||
|
|
9cf9b7b223 | ||
|
|
1fda4865c8 | ||
|
|
f0e55e1096 | ||
|
|
74c71bc8a4 | ||
|
|
0b5609f587 | ||
|
|
2fc5e46f1b | ||
|
|
ef6774325a | ||
|
|
7450cfc627 | ||
|
|
29988c8b1d | ||
|
|
34bb64eddd | ||
|
|
873049c346 | ||
|
|
a722a3a110 | ||
|
|
f2942a7f92 | ||
|
|
ae367d3645 | ||
|
|
6022e96b4d | ||
|
|
2286ef4b36 | ||
|
|
0dd96ebc57 | ||
|
|
06b99c8ef2 | ||
|
|
b357a212dd | ||
|
|
2e704f996b | ||
|
|
284547080c | ||
|
|
07e3da08d0 | ||
|
|
dfd79bde56 | ||
|
|
317d6482e4 | ||
|
|
5f51abd142 | ||
|
|
b9288efc4a | ||
|
|
c90fb34d6d | ||
|
|
75abd94bf5 | ||
|
|
55e48112f8 | ||
|
|
4b65570ba5 | ||
|
|
9aefda7c1a | ||
|
|
bb485009c3 | ||
|
|
24fe68d925 | ||
|
|
3780d9cda8 | ||
|
|
e10703cd7b | ||
|
|
824afb84c9 | ||
|
|
aee33863c8 | ||
|
|
90c29d8f23 | ||
|
|
e686911210 | ||
|
|
8bb830824b | ||
|
|
a6a0cb4331 | ||
|
|
2200d8d173 | ||
|
|
c34a000e78 | ||
|
|
fb51cdaca3 | ||
|
|
50423936b2 | ||
|
|
49994c8915 | ||
|
|
17dbc7e055 | ||
|
|
312c386cda | ||
|
|
96952b92f8 | ||
|
|
3e2333e5ed | ||
|
|
fc7c919b43 | ||
|
|
c7e86f9374 | ||
|
|
5da9376222 | ||
|
|
5461565945 | ||
|
|
bee06cf264 | ||
|
|
cd238fe567 | ||
|
|
09b06509a8 | ||
|
|
e771239608 | ||
|
|
e0af160213 | ||
|
|
e5380d646c | ||
|
|
621c3eebf3 | ||
|
|
4a94f76ec3 | ||
|
|
c0ddd5c5d8 | ||
|
|
a6e55e3528 | ||
|
|
ffeb16eb66 | ||
|
|
a802d8b19a | ||
|
|
555d10dea7 | ||
|
|
3f2355a36a | ||
|
|
bceb298edb | ||
|
|
47b9466fad | ||
|
|
8f80f875b5 | ||
|
|
2941bbae2d | ||
|
|
8842e450db | ||
|
|
f5737aa3cc | ||
|
|
b8fd3d2d8e | ||
|
|
387250cd83 | ||
|
|
433d83221a | ||
|
|
3151804cf8 | ||
|
|
f66e9af515 | ||
|
|
f92abd2ba5 | ||
|
|
700c5bf5b5 | ||
|
|
87c37bb182 | ||
|
|
9cc9a491a8 | ||
|
|
76a8a1637f | ||
|
|
568f6424e5 | ||
|
|
f32a8f7ea0 | ||
|
|
e0b50c8a70 | ||
|
|
0294f5ec55 | ||
|
|
53987e31aa | ||
|
|
f59858543c | ||
|
|
cae7be1a60 | ||
|
|
4ccfe242e8 | ||
|
|
811963ad26 | ||
|
|
41d932017b | ||
|
|
7a24407ecc | ||
|
|
df68c211b5 | ||
|
|
2ca1ea92ff | ||
|
|
8f546d89b4 | ||
|
|
6d3cb5bcd3 | ||
|
|
ca5b0c56f0 | ||
|
|
0d58b93a84 | ||
|
|
b7548e68a8 | ||
|
|
2f3e8c2bd3 | ||
|
|
fc2a8fd59a | ||
|
|
47b24beb6c | ||
|
|
895ff6f452 | ||
|
|
fce7f5c56b | ||
|
|
b5e8dd5edc | ||
|
|
d4fabc0464 | ||
|
|
971d6aafd4 | ||
|
|
0457824193 | ||
|
|
40ad3a1bf9 | ||
|
|
de2680152b | ||
|
|
541ba67daf | ||
|
|
6576d894a3 | ||
|
|
8e1dade106 | ||
|
|
2ebdd028cf | ||
|
|
b5a48f9f6c | ||
|
|
d70ca981c9 | ||
|
|
4b719d7443 | ||
|
|
5b9f6b2192 | ||
|
|
4e242e9211 | ||
|
|
94f3c76039 | ||
|
|
67a7c5601a | ||
|
|
4ea0c0936d | ||
|
|
7bd9a79b12 | ||
|
|
bfb560045c | ||
|
|
c5da46f125 | ||
|
|
10fdd01dc8 | ||
|
|
1e9e781c12 | ||
|
|
24b38126ec | ||
|
|
3577a6989d | ||
|
|
48c49c54e2 | ||
|
|
d4993c0ba4 | ||
|
|
213b7c57ee | ||
|
|
b9b1f2e7f0 | ||
|
|
cdd5210bb7 | ||
|
|
bc90a79581 | ||
|
|
e6e1260bc2 | ||
|
|
1d555e724b | ||
|
|
e6fe480a30 | ||
|
|
91a06063b4 | ||
|
|
49fc496c12 | ||
|
|
94abeeabb9 | ||
|
|
b7e506341f | ||
|
|
fba3fd81fa | ||
|
|
e875c0de26 | ||
|
|
43f058df6d | ||
|
|
01c0ef0013 | ||
|
|
a46fae0687 | ||
|
|
5a646a0d20 | ||
|
|
9a73dcad53 | ||
|
|
b605cf74d2 | ||
|
|
220376b6e2 | ||
|
|
cfb6bf95a3 | ||
|
|
3905843257 | ||
|
|
501a164eba | ||
|
|
9e725cb28a | ||
|
|
e02cdc0c54 | ||
|
|
0038720c01 | ||
|
|
d4279559fa | ||
|
|
1ea9d7d4de | ||
|
|
f7abd084c6 | ||
|
|
d82e0ef5c2 | ||
|
|
3027856c7b | ||
|
|
9de7bd666d | ||
|
|
92b51563b5 | ||
|
|
389c7aa5af | ||
|
|
bda54a9aed | ||
|
|
09b1239131 | ||
|
|
662f2447e3 | ||
|
|
20005f5bbc | ||
|
|
803a1a5489 | ||
|
|
20ba29590a | ||
|
|
13554a3d83 | ||
|
|
9bfc7c7768 | ||
|
|
045ef7b801 | ||
|
|
e69355f78f | ||
|
|
9d14c841d7 | ||
|
|
f796f01f38 | ||
|
|
350299786d | ||
|
|
445c0959b5 | ||
|
|
0c5aae157a | ||
|
|
2e7d0d4934 | ||
|
|
e9a835ac55 | ||
|
|
b55806017a | ||
|
|
b79ce2a549 | ||
|
|
a155795837 | ||
|
|
e935da461a | ||
|
|
92bf2cf105 | ||
|
|
0840af5277 | ||
|
|
967614f64a | ||
|
|
891d807137 | ||
|
|
ecddbcf240 | ||
|
|
aacf7c4613 | ||
|
|
3a35480c7a | ||
|
|
7c962d2f61 | ||
|
|
dc882d6905 | ||
|
|
15146d900c | ||
|
|
ad5a0e42d0 | ||
|
|
a56e29704e | ||
|
|
f344290368 | ||
|
|
5ae8c936e7 | ||
|
|
5413d2e5a3 | ||
|
|
226a1dc391 | ||
|
|
ec810dd6db | ||
|
|
5493212578 | ||
|
|
45d365f98b | ||
|
|
94324e3f46 | ||
|
|
d2762938b7 | ||
|
|
765d126ee9 | ||
|
|
896c4cbccc | ||
|
|
325074981c | ||
|
|
b26f38a6aa | ||
|
|
d13505b6c6 | ||
|
|
67f9d8f655 | ||
|
|
c353e60913 | ||
|
|
662fb712bc | ||
|
|
28165f7b4e | ||
|
|
dbcfda77d9 | ||
|
|
1dfa7776e1 | ||
|
|
40a9c15345 | ||
|
|
3dc3e066aa | ||
|
|
bbf808c347 | ||
|
|
4556e06fcf | ||
|
|
f605a4e430 | ||
|
|
fa1758b950 | ||
|
|
14903d6a05 | ||
|
|
5b08af2e8d | ||
|
|
6cb05c3762 | ||
|
|
94954177e5 | ||
|
|
3d89df13ef | ||
|
|
15f2d240d6 | ||
|
|
85dfac5c06 | ||
|
|
860bcc02c6 | ||
|
|
4daeeff0e3 | ||
|
|
32de4c9e7c | ||
|
|
f0c123d4c0 | ||
|
|
8c22fe84c2 | ||
|
|
9ff453a50b | ||
|
|
4e6b03d668 | ||
|
|
b635fd9edd | ||
|
|
a387952493 | ||
|
|
867a6d6731 | ||
|
|
b20c9470c7 | ||
|
|
7523c420bd | ||
|
|
98cb33599f | ||
|
|
9281719d74 | ||
|
|
8e04a9f844 | ||
|
|
6df4371df6 | ||
|
|
be13072f0c | ||
|
|
989df7454c | ||
|
|
7e87dbaa39 | ||
|
|
1e5cdfd805 | ||
|
|
bb0006b683 | ||
|
|
786117dd49 | ||
|
|
43ba774e52 | ||
|
|
f7439671bc | ||
|
|
48df51aa3b | ||
|
|
4a77702473 | ||
|
|
15c1a0e5e7 | ||
|
|
9c4b734361 | ||
|
|
5b48a7d92c | ||
|
|
4316be95c2 | ||
|
|
19bc1df15d | ||
|
|
bda2317d8b | ||
|
|
1f07530a54 | ||
|
|
d5f5778492 | ||
|
|
1ecc63dfec | ||
|
|
1e4e8b70d9 | ||
|
|
e45cc16a90 | ||
|
|
c26b4525aa | ||
|
|
ea29d2b2cd | ||
|
|
5559a93d8a | ||
|
|
921123155a | ||
|
|
2a1f2d59ee | ||
|
|
3e70b01ec5 | ||
|
|
b55527739a | ||
|
|
bf97d21c03 | ||
|
|
c554987912 | ||
|
|
6f09dedfad | ||
|
|
0a54360211 | ||
|
|
5c24263779 | ||
|
|
5b609ef9e2 | ||
|
|
36bf8ac911 | ||
|
|
efe3877e3c | ||
|
|
433ca2ecb6 | ||
|
|
fec98e2798 | ||
|
|
c188bee3e3 | ||
|
|
0e2cc609df | ||
|
|
adf391fdec | ||
|
|
5d529edfb1 | ||
|
|
59eeeadbac | ||
|
|
7265e2e88c | ||
|
|
5c019d2aef | ||
|
|
679a3f1915 | ||
|
|
60abb34497 | ||
|
|
94ff77f2f2 | ||
|
|
e2eb95a29f | ||
|
|
10be691b86 | ||
|
|
fcdb6d8a54 | ||
|
|
32502bbd1a | ||
|
|
71ffb0028e | ||
|
|
803c0a6e22 | ||
|
|
8ee4aa6de4 | ||
|
|
91261b2757 | ||
|
|
812c91cc9e | ||
|
|
27da832d6d | ||
|
|
aa554d985d | ||
|
|
804c4bbfa6 | ||
|
|
1a658054ab | ||
|
|
33e39a2471 | ||
|
|
538a856c2f | ||
|
|
877e7efb5f | ||
|
|
bdd43beaf3 | ||
|
|
307d60a0c8 | ||
|
|
12758046e1 | ||
|
|
9aaf87c19b | ||
|
|
400bbbac11 | ||
|
|
226eae97cb | ||
|
|
e5d7b3801d | ||
|
|
4e1630e126 | ||
|
|
40b13c28b8 | ||
|
|
2de621cda0 | ||
|
|
5c5a1eae8a | ||
|
|
8f113b48cc | ||
|
|
949e0b3aff | ||
|
|
448ac9dcd4 | ||
|
|
dd21df3ce4 | ||
|
|
3484389dcd | ||
|
|
ace0977930 | ||
|
|
3df07feb11 | ||
|
|
153d598df3 | ||
|
|
a22e2618a6 | ||
|
|
3582facd34 | ||
|
|
2a079911b5 | ||
|
|
5fae96eb6e | ||
|
|
e3d07bc1a0 | ||
|
|
f53866d0c0 | ||
|
|
e5754ae96f | ||
|
|
294bbad407 | ||
|
|
83e43a42d9 | ||
|
|
2485d57f66 | ||
|
|
65c35b6595 | ||
|
|
1ea5c91fcc | ||
|
|
76c5b9d021 | ||
|
|
229f112ff3 | ||
|
|
fae4365983 | ||
|
|
e7f45ca90e | ||
|
|
3236d33b6a | ||
|
|
6c22e057cc | ||
|
|
7bc10ebc7a | ||
|
|
c3f975aef8 | ||
|
|
0f6bf4ce84 | ||
|
|
33c7e46492 | ||
|
|
7c920edc29 | ||
|
|
e09254eea3 | ||
|
|
e5a15ced01 | ||
|
|
c771b2d75a | ||
|
|
477e84adba | ||
|
|
3482d941e1 | ||
|
|
1bf1a4d5e9 | ||
|
|
a0f69d98df | ||
|
|
4b8b4b73e1 | ||
|
|
5b14cc2937 | ||
|
|
eca5a151a4 | ||
|
|
4efd372223 | ||
|
|
356e9d8a92 | ||
|
|
8838c30ea1 | ||
|
|
8204a1b694 | ||
|
|
bd558875f3 | ||
|
|
898efb69a3 | ||
|
|
31fe4f1bd0 | ||
|
|
dcb3036b70 | ||
|
|
9734d9d7fa | ||
|
|
23c70f609d | ||
|
|
f018137207 | ||
|
|
ad09c1e086 | ||
|
|
31e1ff7154 | ||
|
|
8b8bb37598 | ||
|
|
0aefb0e036 | ||
|
|
5879c492d8 | ||
|
|
ee66d53c54 | ||
|
|
f95c4a4712 | ||
|
|
c8b857465b | ||
|
|
21366afc54 | ||
|
|
b453e0068f | ||
|
|
1e9b5f61fc | ||
|
|
0ce366b5d2 | ||
|
|
3744638e5c | ||
|
|
915c6d4a60 | ||
|
|
bd964adb57 | ||
|
|
dec1f402ed | ||
|
|
b96e1596b2 | ||
|
|
3569838542 | ||
|
|
8c7c4812b2 | ||
|
|
987fd7555e | ||
|
|
63d669f712 | ||
|
|
078b982be1 | ||
|
|
d402c66af2 | ||
|
|
03c3123bb8 | ||
|
|
82c8bdaf0d | ||
|
|
163a6cf3dd | ||
|
|
ab594b6afe | ||
|
|
1f1d590c7a | ||
|
|
fcd01592e1 | ||
|
|
e1205a8a49 | ||
|
|
d7768ab228 | ||
|
|
bf208cdeb6 | ||
|
|
12fea0f6d5 | ||
|
|
bcfb3009c0 | ||
|
|
8a1264e266 | ||
|
|
36c9b040ca | ||
|
|
2eb0593cd2 | ||
|
|
d40003e0cd | ||
|
|
9330a33fe5 | ||
|
|
2e089b5e4a | ||
|
|
edf6003217 | ||
|
|
c0095746af | ||
|
|
33d548b412 | ||
|
|
615ebb7afa | ||
|
|
7a5060478b | ||
|
|
0bd5c6519c | ||
|
|
1052f8a0d4 | ||
|
|
03d211f2a9 | ||
|
|
f6c4239409 | ||
|
|
59897168d4 | ||
|
|
b597ef5d55 | ||
|
|
e3c8066a9e | ||
|
|
78e3e18c42 | ||
|
|
01e028c19f | ||
|
|
d49aaf0f70 | ||
|
|
f7d6c9e7a1 | ||
|
|
015aaa2107 | ||
|
|
bad8656361 | ||
|
|
eb3b74ea34 | ||
|
|
dabc0b812a | ||
|
|
dd1a450b50 | ||
|
|
ead9f83050 | ||
|
|
0fb91f186e | ||
|
|
3e38a959c3 | ||
|
|
535e61d2be | ||
|
|
4885cb24ed | ||
|
|
73da7204e6 | ||
|
|
ff4b41439a | ||
|
|
21b2c2c4aa | ||
|
|
e2b799b815 | ||
|
|
4744aa2b78 | ||
|
|
c04ef88e5c | ||
|
|
4b1dc93bcc | ||
|
|
f58f83c043 | ||
|
|
22b8e5e427 | ||
|
|
cec4476303 | ||
|
|
31015ef5d8 | ||
|
|
600dad2498 | ||
|
|
4ff6243fd6 | ||
|
|
9c0ed88c72 | ||
|
|
93f9b956e4 | ||
|
|
b69ef234ac | ||
|
|
2988429747 | ||
|
|
d4ece25ba6 | ||
|
|
ad85e44d78 | ||
|
|
655194d3e1 | ||
|
|
6eb01d7923 | ||
|
|
ae6475598f | ||
|
|
62499d96df | ||
|
|
4f33cad025 | ||
|
|
2aafefc2fa | ||
|
|
9d5a21a799 | ||
|
|
ce15c4f67d | ||
|
|
01e0758e5d | ||
|
|
e8238d185d | ||
|
|
54c51c0299 | ||
|
|
2b35a2caeb | ||
|
|
9b11238467 | ||
|
|
fa24f22d4d | ||
|
|
ce823507cd | ||
|
|
cd580debb6 | ||
|
|
1e9106badb | ||
|
|
232acc8261 | ||
|
|
55179737f9 | ||
|
|
28c2cdc2c4 | ||
|
|
d85a58feeb | ||
|
|
89a6dea7f9 | ||
|
|
9a518b1eee | ||
|
|
d54cde46e0 | ||
|
|
320df7fe28 | ||
|
|
0ab282400e | ||
|
|
8aa7e05d83 | ||
|
|
b43a67edbd | ||
|
|
9c4d047462 | ||
|
|
ccc0b1a1cf | ||
|
|
78b2f585ae | ||
|
|
39128f8cf8 | ||
|
|
7fe847967d | ||
|
|
f2e7884ade | ||
|
|
0809105bed | ||
|
|
93bf7793cb | ||
|
|
ec969b7f99 | ||
|
|
cb859243a5 | ||
|
|
e04c207c3c | ||
|
|
0f832c9226 | ||
|
|
496cd25d37 | ||
|
|
107785ddf1 | ||
|
|
6bb5e12347 | ||
|
|
928c0761a0 | ||
|
|
30b4b4e7f9 | ||
|
|
f509ae282d | ||
|
|
d9fd4ea245 | ||
|
|
6c947bca07 | ||
|
|
e4514a4ecb | ||
|
|
8c778bb081 | ||
|
|
f1bf3c60e0 | ||
|
|
06f7408cf2 | ||
|
|
e1ca7196c7 | ||
|
|
8f310286f8 | ||
|
|
b97c0392a9 | ||
|
|
9c6620d85d | ||
|
|
ae2ede7994 | ||
|
|
b91008345f | ||
|
|
2f8dfe5810 | ||
|
|
79dcdc0491 | ||
|
|
b0fb398833 | ||
|
|
6e64deec08 | ||
|
|
b97c1dbd56 | ||
|
|
d75e39ac61 | ||
|
|
88dd4f7329 | ||
|
|
e8d60e0fcf | ||
|
|
0d94548a09 | ||
|
|
4c3ff8f184 | ||
|
|
49d84f7cfc | ||
|
|
0e8cca0869 | ||
|
|
0eba655d2d | ||
|
|
cece39e439 | ||
|
|
cfd381134f | ||
|
|
7676c17f27 | ||
|
|
55cf19c4de | ||
|
|
50cebe6753 | ||
|
|
237a3c54cf | ||
|
|
5ca91f9ced | ||
|
|
df600200a7 | ||
|
|
089e8130e4 | ||
|
|
e78e8fb56a | ||
|
|
669e420cc4 | ||
|
|
6143bb1395 | ||
|
|
a8c1965b90 | ||
|
|
5507550c53 | ||
|
|
4bd1a61353 | ||
|
|
edd1d0f3a4 | ||
|
|
0919b7ca67 | ||
|
|
1902dd2e3a | ||
|
|
7cb340b7c9 | ||
|
|
92b601aba9 | ||
|
|
350fdbfe50 | ||
|
|
548580d817 | ||
|
|
149a1f5639 | ||
|
|
197b083fbc | ||
|
|
a7648e78fb | ||
|
|
50202e1246 | ||
|
|
a4338de38c | ||
|
|
d08d101eba | ||
|
|
32f7847bca | ||
|
|
1bb990357f | ||
|
|
a9c28885f3 | ||
|
|
d776bd3a4f | ||
|
|
5d6fda932d | ||
|
|
c1a6ae2b48 | ||
|
|
78606a35c9 | ||
|
|
1dd3de0d04 | ||
|
|
ece14ff4b5 | ||
|
|
e5653fb63b | ||
|
|
3f61ea7104 | ||
|
|
2ec866d2c5 | ||
|
|
26c61734da | ||
|
|
e1246c3741 | ||
|
|
2d924f8a65 | ||
|
|
00d9b78035 | ||
|
|
612b21cbe7 | ||
|
|
8139695d46 | ||
|
|
e8146dd130 | ||
|
|
44af37bc2b | ||
|
|
beb05a71c8 | ||
|
|
74ea006d92 | ||
|
|
335ba99b60 | ||
|
|
8dcefd5ff9 | ||
|
|
28833d864c | ||
|
|
a5af91d0d3 | ||
|
|
4deb735425 | ||
|
|
f89d9a4d6b | ||
|
|
d1b29d58df | ||
|
|
ba8d4f9bd6 | ||
|
|
0b7c277ec2 | ||
|
|
1aec55d960 | ||
|
|
595427842c | ||
|
|
41245f5087 | ||
|
|
b684ff7844 | ||
|
|
ea7c326017 | ||
|
|
f85c1cefb4 | ||
|
|
9005ce3255 | ||
|
|
efc991547e | ||
|
|
8bebd37759 | ||
|
|
e52d0ee1de | ||
|
|
ff56881946 | ||
|
|
5a8bb48951 | ||
|
|
318ae55e35 | ||
|
|
99222e70f7 | ||
|
|
5562e66a2d | ||
|
|
28f46c8d2d | ||
|
|
bf9c6ab7b4 | ||
|
|
14c1926081 | ||
|
|
d7e5dfc5b6 | ||
|
|
8856d284c0 | ||
|
|
14ff3ad3d0 | ||
|
|
1d814c606d | ||
|
|
82f483f559 | ||
|
|
a49c85a610 | ||
|
|
8e3699eca6 | ||
|
|
e3c074b881 | ||
|
|
9addfc13ce | ||
|
|
d27a3c52b9 | ||
|
|
2db4268ba8 | ||
|
|
0dbfd3a636 | ||
|
|
a908fafad7 | ||
|
|
8688ca9249 | ||
|
|
8a622c3a9a | ||
|
|
bf5becc7d5 | ||
|
|
216a4b7c9d | ||
|
|
cbe20c7587 | ||
|
|
edf46e9570 | ||
|
|
b1f90976a3 | ||
|
|
4ac1148e9f | ||
|
|
84a91a14a4 | ||
|
|
94c02695fd | ||
|
|
d8c88bedf3 | ||
|
|
7d557fd759 | ||
|
|
551a3666eb | ||
|
|
542fb6167e | ||
|
|
ee222055f1 | ||
|
|
03dd914edc | ||
|
|
a6bcad8f48 | ||
|
|
6f446f6e55 | ||
|
|
134a75ef38 | ||
|
|
e6d8e974ce | ||
|
|
176856bc9b | ||
|
|
7a106d9b0d | ||
|
|
de5d31184f | ||
|
|
65d46518c0 | ||
|
|
7a0ee56c80 | ||
|
|
1c0b54e52a | ||
|
|
148a3f5735 | ||
|
|
8996208084 | ||
|
|
7eb869af85 | ||
|
|
2b26161000 | ||
|
|
e5b54a3143 | ||
|
|
a24705d784 | ||
|
|
9d275262e2 | ||
|
|
8183cb6262 | ||
|
|
46cffb3bcf | ||
|
|
738ae1f3fa | ||
|
|
fb25ee0200 | ||
|
|
2a4cbe2f57 | ||
|
|
0e3cb791b5 | ||
|
|
ae5ac257dd | ||
|
|
3cd2e1a6aa | ||
|
|
08773f8fe9 | ||
|
|
8a7d42db0b | ||
|
|
daa576c47a | ||
|
|
b80f4aeb85 | ||
|
|
60b5864e1d | ||
|
|
8d1444a914 | ||
|
|
7e79d1351c | ||
|
|
c120e37897 | ||
|
|
d990eb0717 | ||
|
|
d2f9aea273 | ||
|
|
0bfd19fe83 | ||
|
|
5cc808286a | ||
|
|
2668484163 | ||
|
|
7596de5903 | ||
|
|
5675f2a142 | ||
|
|
c2e1451997 | ||
|
|
4e13f615aa | ||
|
|
37e1eafdbb | ||
|
|
2b03a115d2 | ||
|
|
85cc906f14 | ||
|
|
d00fa51f4c | ||
|
|
518c75b548 | ||
|
|
ad2fda2522 | ||
|
|
1dcc047624 | ||
|
|
1ee6fad705 | ||
|
|
219f9de778 | ||
|
|
54d320ce9f | ||
|
|
996098081e | ||
|
|
8a130deae5 | ||
|
|
1d67218d1f | ||
|
|
76521ef1d4 | ||
|
|
7e643d36c0 | ||
|
|
11782ae0de | ||
|
|
24baea734f | ||
|
|
87737a7064 | ||
|
|
4876b43fe9 | ||
|
|
57fd19efaa | ||
|
|
e30f2c99c2 | ||
|
|
55ca2d0c96 | ||
|
|
74a56e2bae | ||
|
|
444f9d8d76 | ||
|
|
300efb2b80 | ||
|
|
e0e69bbeec | ||
|
|
276bdaf0d3 | ||
|
|
cde0c25bfe | ||
|
|
73a098a499 | ||
|
|
eaf5888ae0 | ||
|
|
2a75022403 | ||
|
|
fe769afa22 | ||
|
|
5c24e18639 | ||
|
|
cc960215fc | ||
|
|
ec226c9b83 | ||
|
|
ee7ee4065c | ||
|
|
40beaabc14 | ||
|
|
e900a16caf | ||
|
|
05c2d669a9 | ||
|
|
cc16d2ce29 | ||
|
|
4800435d1a | ||
|
|
f9654e5654 | ||
|
|
a77e7a029e | ||
|
|
bd507eecfa | ||
|
|
9802686b32 | ||
|
|
4d1c17e17e | ||
|
|
9da63d4e85 | ||
|
|
0a9358c9fc | ||
|
|
b480b6ef77 | ||
|
|
ccca871ae6 | ||
|
|
2d48b4633c | ||
|
|
f6149e4d18 | ||
|
|
6555c9294b | ||
|
|
4c85d6d3ec | ||
|
|
8e5892e2fd | ||
|
|
59d326295f | ||
|
|
dd2a2442c4 | ||
|
|
7268f6943c | ||
|
|
a0ca68b6b8 | ||
|
|
cf9c968756 | ||
|
|
25f64a39a0 | ||
|
|
57a0bb2f95 | ||
|
|
01eea3503b | ||
|
|
85b69adbf8 | ||
|
|
52c700ff0c | ||
|
|
5d4d630580 | ||
|
|
455194a3b8 | ||
|
|
0533e1cc1e | ||
|
|
b13a2c17c5 | ||
|
|
2f40c85691 | ||
|
|
ab312967fe | ||
|
|
2c07d62b3c | ||
|
|
e3a1fbb047 | ||
|
|
9de7873bc8 | ||
|
|
b7097daad4 | ||
|
|
52082ff3f9 | ||
|
|
b78c938201 | ||
|
|
dde9c91dcf | ||
|
|
9d0dbe488a | ||
|
|
6d2416ecbe | ||
|
|
f00101716b | ||
|
|
b47cdfea26 | ||
|
|
f95aebb4f9 | ||
|
|
d869d82027 | ||
|
|
54d026f656 | ||
|
|
68c89df8a1 | ||
|
|
dd159b4a73 | ||
|
|
299925c22f | ||
|
|
7d83d46270 | ||
|
|
2fffa0bb6f | ||
|
|
0a6e54231e | ||
|
|
6b2a3b187b | ||
|
|
e282b6e29f | ||
|
|
7abca147c4 | ||
|
|
71ba12c539 | ||
|
|
b97415eed4 | ||
|
|
9f8c25515f | ||
|
|
011b02f886 | ||
|
|
fa8693480a | ||
|
|
fc75a1b185 | ||
|
|
6e9074f89e | ||
|
|
6aff964ea0 | ||
|
|
57aae3e103 | ||
|
|
60a39a7241 | ||
|
|
1a52deeea6 | ||
|
|
956546acc7 | ||
|
|
e9d4482e42 | ||
|
|
d26abda426 | ||
|
|
81d27b3c56 | ||
|
|
72ddde869c | ||
|
|
cd19cf520f | ||
|
|
a434c3d1b6 | ||
|
|
6aa7f3493b | ||
|
|
32b122bf42 | ||
|
|
272d496dfe | ||
|
|
a6661f0ac1 | ||
|
|
68f5925ed6 | ||
|
|
3b48e82990 | ||
|
|
c26875cb55 | ||
|
|
e3f6ecca97 | ||
|
|
82b7207295 | ||
|
|
259d713315 | ||
|
|
c79882b766 | ||
|
|
fa20913b56 | ||
|
|
271013e0dc | ||
|
|
6c5cc2bfee | ||
|
|
83e7ea2a0d | ||
|
|
6ef2973fe2 | ||
|
|
e55cbef182 | ||
|
|
3758da2ec8 | ||
|
|
121eb4496b | ||
|
|
8bc0037938 | ||
|
|
54dfb2434a | ||
|
|
b145865407 | ||
|
|
83332c1a42 | ||
|
|
d2ff558da7 | ||
|
|
34954fe671 | ||
|
|
ab1fb093d1 | ||
|
|
2a554ae33c | ||
|
|
caf024bc98 | ||
|
|
e555f83c97 | ||
|
|
c16679863b | ||
|
|
f800e5124d | ||
|
|
f24e70a435 | ||
|
|
3482b4467c | ||
|
|
e7ba6b9ebb | ||
|
|
bbe5f8a389 | ||
|
|
fce4d59dc1 | ||
|
|
f583900da9 | ||
|
|
68f97b74ad | ||
|
|
9ab2cba870 | ||
|
|
99c035d9ca | ||
|
|
6c82d1bae7 | ||
|
|
03e5965236 | ||
|
|
c3a583c813 | ||
|
|
e123afea97 | ||
|
|
7ad4829203 | ||
|
|
a650b8e657 | ||
|
|
a4db2c2fca | ||
|
|
4720f559c3 | ||
|
|
56278434a8 | ||
|
|
4a67fc9a4a | ||
|
|
257a2f636b | ||
|
|
c6e7eaf07c | ||
|
|
cd7457480b | ||
|
|
5fb3d5426c | ||
|
|
724f1144ac | ||
|
|
8e4d818a60 | ||
|
|
d0e53ca863 | ||
|
|
f89015971f | ||
|
|
388f54e148 | ||
|
|
ff6145c015 | ||
|
|
3e6b3dd273 | ||
|
|
c58bb42f57 | ||
|
|
afe0ce81a5 | ||
|
|
e10abba08d | ||
|
|
b5672f1565 | ||
|
|
de901ef058 | ||
|
|
b1affce867 | ||
|
|
8bbd368576 | ||
|
|
ce67093a62 | ||
|
|
15b482e375 | ||
|
|
e540ab5726 | ||
|
|
ce9cd5bee5 | ||
|
|
b14b1ee441 | ||
|
|
06b8210ea1 | ||
|
|
f2dee12d93 | ||
|
|
5eac2a8403 | ||
|
|
5791f32a2d | ||
|
|
ffd089ea1c | ||
|
|
8eae25501c | ||
|
|
9a17de9166 | ||
|
|
74cd084315 | ||
|
|
a0c97d110c | ||
|
|
8125370d8e | ||
|
|
add0ee6059 | ||
|
|
e21aa14fac | ||
|
|
3f02f15e47 | ||
|
|
1e4f337584 | ||
|
|
f5191b855f | ||
|
|
f32a546855 | ||
|
|
e4370f7ee7 | ||
|
|
c9ae0ab003 | ||
|
|
fc48e6ee7c | ||
|
|
9a7717ed9d | ||
|
|
c532689266 | ||
|
|
d82b6183d6 | ||
|
|
a9aebba7f7 | ||
|
|
9098528a16 | ||
|
|
55c335849a | ||
|
|
4779fa84f9 | ||
|
|
0b1832ddc2 | ||
|
|
e91b2815fa | ||
|
|
c2cc1aacf6 | ||
|
|
41badb9686 | ||
|
|
45e82d0952 | ||
|
|
d93c6ef6ce | ||
|
|
fe414722ac | ||
|
|
8423875b37 | ||
|
|
47acf53974 | ||
|
|
67516aa44f | ||
|
|
6e4e79afc3 | ||
|
|
22f807fc6f | ||
|
|
b2ef87ae51 | ||
|
|
0774c8bbce | ||
|
|
cf6f67045b | ||
|
|
353411422e | ||
|
|
c70aab0a38 | ||
|
|
fb2f652319 | ||
|
|
178a3d8410 | ||
|
|
c8fbd4465c | ||
|
|
f2eaeac78c | ||
|
|
6ab417ccd7 | ||
|
|
3e624fb237 | ||
|
|
8abc3472e3 | ||
|
|
ec7b211c41 | ||
|
|
6ab1f1ff23 | ||
|
|
736038f8ef | ||
|
|
e4c82e526e | ||
|
|
a3d899123d | ||
|
|
b055728fd5 | ||
|
|
a165881049 | ||
|
|
e01292c167 | ||
|
|
956bf4f2d0 | ||
|
|
e100d1d9d0 | ||
|
|
ad824e4e84 | ||
|
|
3fac5a43d7 | ||
|
|
07aad11081 | ||
|
|
28cd95c374 | ||
|
|
af380c8912 | ||
|
|
8d3c2859e4 | ||
|
|
e7c787b343 | ||
|
|
cae423e543 | ||
|
|
7f9eb9e540 | ||
|
|
3465c9ea64 | ||
|
|
ed097b3b9c | ||
|
|
bca38c997e | ||
|
|
0e2a7240c3 | ||
|
|
51a1482407 | ||
|
|
0e3fef2fb3 | ||
|
|
bcacfc0510 | ||
|
|
c4ae423238 | ||
|
|
d4b53ea73d | ||
|
|
e953444fa5 | ||
|
|
74993b6536 | ||
|
|
7b323bd4f4 | ||
|
|
7cbde77496 | ||
|
|
08ed5bb68a | ||
|
|
9e7aee2061 | ||
|
|
740d7ce39a | ||
|
|
4065511abb | ||
|
|
2ad03eea40 | ||
|
|
406f5da19d | ||
|
|
5947e017fd | ||
|
|
44c63c2abd | ||
|
|
77529ce550 | ||
|
|
c30e500731 | ||
|
|
b403cc7f96 | ||
|
|
3b1174eff6 | ||
|
|
c46c1ae6ef | ||
|
|
9dbe572abe | ||
|
|
9f32a08b28 | ||
|
|
47abe6d950 | ||
|
|
3a7d2a133e | ||
|
|
b0a7a2579a | ||
|
|
4e57ddbce1 | ||
|
|
8f30aa0017 | ||
|
|
f7f2f977d5 | ||
|
|
188964c911 | ||
|
|
e3a2ef011f | ||
|
|
d44a5843d3 | ||
|
|
47dbca2921 | ||
|
|
1a9d2eac29 | ||
|
|
73a323f2a2 | ||
|
|
315cbf9e79 | ||
|
|
7ecd095c45 | ||
|
|
7ba9e05542 | ||
|
|
80be564274 | ||
|
|
2626693241 | ||
|
|
a16fae676f | ||
|
|
91b9e374a0 | ||
|
|
be96355468 | ||
|
|
b924c5b562 | ||
|
|
73360ab95c | ||
|
|
5156b3894c | ||
|
|
11a6d400c5 | ||
|
|
fd21c46f64 | ||
|
|
c80e18cd46 | ||
|
|
1bda2d21ad | ||
|
|
6f45478f18 | ||
|
|
75d72d960b | ||
|
|
cdeaca8682 | ||
|
|
dfc08a869e | ||
|
|
c8641d7d15 | ||
|
|
7ae73e677f | ||
|
|
f259e2fa1c | ||
|
|
509a852f18 | ||
|
|
3eb5abe6a7 | ||
|
|
4e69ae83dd | ||
|
|
bb941950d5 | ||
|
|
1a40cd0477 | ||
|
|
c246e2d55e | ||
|
|
95f6e7b728 | ||
|
|
47e26cce19 | ||
|
|
086ef80e8e | ||
|
|
48a8d297d1 | ||
|
|
ddfbfc734c | ||
|
|
dc630c540a | ||
|
|
efc9a01cf1 | ||
|
|
04fa18dc61 | ||
|
|
a681f9b645 | ||
|
|
54977c79cd | ||
|
|
1fdcd6768c | ||
|
|
137ec3054d | ||
|
|
63214ea2af | ||
|
|
e0cfcdd505 | ||
|
|
2235f21698 | ||
|
|
c3a4440025 | ||
|
|
5468377680 | ||
|
|
dba3b59929 | ||
|
|
78073e1b6f | ||
|
|
d40b0a9973 | ||
|
|
235415cf49 | ||
|
|
019bff8282 | ||
|
|
28ab4a33d6 | ||
|
|
4f4f1dd2fa | ||
|
|
8ff7ee4297 | ||
|
|
4e94bac0ae | ||
|
|
fe29f64dd3 | ||
|
|
2b083dd191 | ||
|
|
777c90236e | ||
|
|
8d8ac76e85 | ||
|
|
3e021c2579 | ||
|
|
31b43002e7 | ||
|
|
64c451e01f | ||
|
|
e34cd9a6cf | ||
|
|
f52edfc545 | ||
|
|
befa1310df | ||
|
|
3774f234f0 | ||
|
|
114dcd5653 | ||
|
|
83097ff942 | ||
|
|
6d72a3cb0c | ||
|
|
d5886525cc | ||
|
|
f8952d56e1 | ||
|
|
bd1e997f15 | ||
|
|
8895862acf | ||
|
|
7c371cf32a | ||
|
|
e85f8bdf29 | ||
|
|
7d4b7b7f47 | ||
|
|
496b5f9ed0 | ||
|
|
281f6e59db | ||
|
|
80a8fa62d5 | ||
|
|
bebe9dbed2 | ||
|
|
46fa564a8a | ||
|
|
0df8b19568 | ||
|
|
ac9554d6dc | ||
|
|
799242c617 | ||
|
|
8f99421e02 | ||
|
|
8e8081c408 | ||
|
|
3031d31efe | ||
|
|
1b6a9c3a20 | ||
|
|
f7c2df2513 | ||
|
|
723d068c51 | ||
|
|
ca58c896dd | ||
|
|
d856c81896 | ||
|
|
6d2cd77b5a | ||
|
|
80b3dbbd48 | ||
|
|
74ef761529 | ||
|
|
ee00736307 | ||
|
|
60d9ead65a | ||
|
|
540379957b | ||
|
|
376bcba442 | ||
|
|
433a7cc88c | ||
|
|
c98514ae44 | ||
|
|
ac65ea3ff3 | ||
|
|
fcf6a16f3d | ||
|
|
0cd489a78b | ||
|
|
8a034f4f6c | ||
|
|
1bb4412ff3 | ||
|
|
16c57ddb33 | ||
|
|
4804237fa7 | ||
|
|
eeb9cb3c2b | ||
|
|
83086439cc | ||
|
|
8f20b95d56 | ||
|
|
a6b473227f | ||
|
|
8b5062a3a1 | ||
|
|
e3621eb057 | ||
|
|
89bf31030b | ||
|
|
4d2d52c3db | ||
|
|
220890a0c9 | ||
|
|
bc547080b8 | ||
|
|
75810224ed | ||
|
|
8e63d3cb72 | ||
|
|
fabbac885f | ||
|
|
2e52801de5 | ||
|
|
b426d24e45 | ||
|
|
b6423f8c4d | ||
|
|
15f37cc292 | ||
|
|
7b804ffbae | ||
|
|
1095628b8a | ||
|
|
7c1d7c9f90 | ||
|
|
345bf7cc29 | ||
|
|
ff43615ab1 | ||
|
|
fd99d67c33 | ||
|
|
51270ce4a2 | ||
|
|
cd27f9db57 | ||
|
|
ede34b6a4d | ||
|
|
40580731cd | ||
|
|
9bb15c76e9 | ||
|
|
b7640209a8 | ||
|
|
1417fe7283 | ||
|
|
7df36e476e | ||
|
|
f939eb5625 | ||
|
|
0b4ae53a7e | ||
|
|
e04f092232 | ||
|
|
05cce994c3 | ||
|
|
0a9ea29fd2 | ||
|
|
696a619d0e | ||
|
|
ea420ee29f | ||
|
|
2708cadd40 | ||
|
|
657333199b | ||
|
|
6a30c394b1 | ||
|
|
93519c04f9 | ||
|
|
9dbb68df9e | ||
|
|
77131e8f24 | ||
|
|
c53fa3ba32 | ||
|
|
0b084dd51a | ||
|
|
2f53491343 | ||
|
|
845dedca32 | ||
|
|
0608b5939b | ||
|
|
ea7c8795f5 | ||
|
|
ac75d7cff4 | ||
|
|
fb7658c8ca | ||
|
|
98332cb616 | ||
|
|
433503cf8a | ||
|
|
55dd936829 | ||
|
|
08cef003f7 | ||
|
|
f4d9e05d05 | ||
|
|
2a0cad3c85 | ||
|
|
b3dd44d41b | ||
|
|
3da2b1482c | ||
|
|
d8ef79dff7 | ||
|
|
af8f0b61b6 | ||
|
|
42ba5d92c3 | ||
|
|
748e35d50f | ||
|
|
4ccc7ee941 | ||
|
|
95d9a374b2 | ||
|
|
994c68f648 | ||
|
|
9011e0d22c | ||
|
|
6bd22292b6 | ||
|
|
eddef947a4 | ||
|
|
98d4f1891c |
No files matched your search
@@ -1 +0,0 @@
|
||||
Dockerfile
|
||||
@@ -0,0 +1 @@
|
||||
*.py diff=python
|
||||
@@ -1,44 +0,0 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
pull: true
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
+11
-18
@@ -1,21 +1,14 @@
|
||||
# Python
|
||||
mnexec
|
||||
*.pyc
|
||||
*~
|
||||
*.1
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
\#*\#
|
||||
mininet.egg-info
|
||||
build
|
||||
dist
|
||||
Mini_NDN.egg-info
|
||||
*.pyc
|
||||
doc/html
|
||||
doc/latex
|
||||
trunk
|
||||
|
||||
# Docs
|
||||
docs/html
|
||||
docs/latex
|
||||
docs/_build
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
dl
|
||||
*.apconf
|
||||
|
||||
# Vagrant
|
||||
.vagrant
|
||||
|
||||
venv/
|
||||
.vscode
|
||||
@@ -0,0 +1,301 @@
|
||||
# lint Python modules using external checkers.
|
||||
#
|
||||
# This is the main checker controlling the other ones and the reports
|
||||
# generation. It is itself both a raw checker and an astng checker in order
|
||||
# to:
|
||||
# * handle message activation / deactivation at the module level
|
||||
# * handle some basic but necessary stats'data (number of classes, methods...)
|
||||
#
|
||||
[MASTER]
|
||||
|
||||
# Specify a configuration file.
|
||||
#rcfile=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Profiled execution.
|
||||
profile=no
|
||||
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
ignore=CVS
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time.
|
||||
#enable=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once).
|
||||
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
|
||||
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
|
||||
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
|
||||
locally-disabled
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html
|
||||
output-format=colorized
|
||||
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
|
||||
|
||||
# Include message's id in output
|
||||
include-ids=yes
|
||||
|
||||
# Put messages in a separate file for each module / package specified on the
|
||||
# command line instead of printing them on stdout. Reports (if any) will be
|
||||
# written in a file name "pylint_global.[txt|html]".
|
||||
files-output=no
|
||||
|
||||
# Tells wether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highes
|
||||
# note). You have access to the variables errors warning, statement which
|
||||
# respectivly contain the number of errors / warnings messages and the total
|
||||
# number of statements analyzed. This is used by the global evaluation repor
|
||||
# (R0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Add a comment according to your evaluation note. This is used by the global
|
||||
# evaluation report (R0004).
|
||||
comment=no
|
||||
|
||||
# Enable the report(s) with the given id(s).
|
||||
#enable-report=
|
||||
|
||||
# Disable the report(s) with the given id(s).
|
||||
#disable-report=
|
||||
|
||||
|
||||
# checks for :
|
||||
# * doc strings
|
||||
# * modules / classes / functions / methods / arguments / variables name
|
||||
# * number of arguments, local variables, branchs, returns and statements in
|
||||
# functions, methods
|
||||
# * required module attributes
|
||||
# * dangerous default values as arguments
|
||||
# * redefinition of function / method / class
|
||||
# * uses of the global statemen
|
||||
#
|
||||
[BASIC]
|
||||
|
||||
# Required attributes for module, separated by a comma
|
||||
required-attributes=
|
||||
|
||||
# Regular expression which should only match functions or classes name which do
|
||||
# not require a docstring
|
||||
no-docstring-rgx=__.*__
|
||||
|
||||
# Regular expression which should only match correct module names
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Regular expression which should only match correct module level names
|
||||
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
|
||||
# Regular expression which should only match correct class names
|
||||
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Regular expression which should only match correct function names
|
||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct method names
|
||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct instance attribute names
|
||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct argument names
|
||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct list comprehension /
|
||||
# generator expression variable names
|
||||
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=i,j,k,ex,Run,_
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=foo,bar,baz,toto,tutu,tata
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma
|
||||
bad-functions=map,filter,apply,inpu
|
||||
|
||||
|
||||
# try to find bugs in the code using type inference
|
||||
#
|
||||
[TYPECHECK]
|
||||
|
||||
# Tells wether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# List of classes names for which member attributes should not be checked
|
||||
# (useful for classes with attributes dynamicaly set).
|
||||
ignored-classes=SQLObjec
|
||||
|
||||
# When zope mode is activated, add a predefined set of Zope acquired attributes
|
||||
# to generated-members.
|
||||
zope=no
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E0201 when accessed.
|
||||
generated-members=REQUEST,acl_users,aq_paren
|
||||
|
||||
|
||||
# checks for
|
||||
# * unused variables / imports
|
||||
# * undefined variables
|
||||
# * redefinition of variable from builtins or from an outer scope
|
||||
# * use of variable before assigmen
|
||||
#
|
||||
[VARIABLES]
|
||||
|
||||
# Tells wether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching names used for dummy variables (i.e. not used).
|
||||
dummy-variables-rgx=_|dummy
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember tha
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
|
||||
# checks for :
|
||||
# * methods without self as first argumen
|
||||
# * overridden methods signature
|
||||
# * access only to existant members via self
|
||||
# * attributes not defined in the __init__ method
|
||||
# * supported interfaces implementation
|
||||
# * unreachable code
|
||||
#
|
||||
[CLASSES]
|
||||
|
||||
# List of interface methods to ignore, separated by a comma. This is used for
|
||||
# instance to not check methods defines in Zope's Interface base class.
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp,build
|
||||
|
||||
|
||||
# checks for sign of poor/misdesign:
|
||||
# * number of methods, attributes, local variables...
|
||||
# * size, complexity of functions, methods
|
||||
#
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method
|
||||
max-args=5
|
||||
|
||||
# Maximum number of locals for function / method body
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of return / yield for function / method body
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
max-branchs=12
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
max-statements=50
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
|
||||
# checks for
|
||||
# * external modules dependencies
|
||||
# * relative / wildcard imports
|
||||
# * cyclic imports
|
||||
# * uses of deprecated modules
|
||||
#
|
||||
[IMPORTS]
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report R0402 must not be disabled)
|
||||
import-graph=
|
||||
|
||||
# Create a graph of external dependencies in the given file (report R0402 mus
|
||||
# not be disabled)
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report R0402 mus
|
||||
# not be disabled)
|
||||
int-import-graph=
|
||||
|
||||
|
||||
# checks for :
|
||||
# * unauthorized constructions
|
||||
# * strict indentation
|
||||
# * line length
|
||||
# * use of <> instead of !=
|
||||
#
|
||||
[FORMAT]
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# Maximum number of lines in a module
|
||||
# XXX 1500 -> 4000 for miniedit.py
|
||||
max-module-lines=4000
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
|
||||
# checks for:
|
||||
# * warning notes in the code like FIXME, XXX
|
||||
# * PEP 263: source code with non ascii character but no encoding declaration
|
||||
#
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,XXX,TODO
|
||||
|
||||
|
||||
# checks for similarities and duplicated code. This computation may be
|
||||
# memory / CPU intensive, so you should disable it if you experiments some
|
||||
# problems.
|
||||
#
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
Mini-NDN Authors
|
||||
=================
|
||||
|
||||
The following lists maintainers, primary developers, and all much-appreciated contributors to Mini-NDN in alphabetic order.
|
||||
The specific contributions of individual authors can be obtained from the git history of the [official Mini-NDN repository](https://github.com/named-data/mini-ndn).
|
||||
If you would like to become a contributor to the official repository, please follow the recommendations in https://github.com/named-data/.github/blob/master/CONTRIBUTING.md.
|
||||
|
||||
* Alexander Afanasyev <https://users.cs.fiu.edu/~afanasyev>
|
||||
* Muktadir R. Chowdhury <https://github.com/alvyC>
|
||||
* Damian Coomes <https://github.com/dmcoomes>
|
||||
* ***(Maintainer)*** Saurab Dulal <https://dulalsaurab.github.io>
|
||||
* Laqin Fan <https://github.com/laqinfan>
|
||||
* ***(Former Maintainer)*** Ashlesh Gawande <https://www.linkedin.com/in/agawande>
|
||||
* Nicholas Gordon <https://github.com/gorgonical>
|
||||
* Giovanni Grieco <https://github.com/GiovanniGrieco>
|
||||
* ***(Maintainer)*** Alexander Lane <https://github.com/awlane>
|
||||
* Vince Lehman <http://vslehman.com>
|
||||
* Philipp Moll <https://github.com/phylib>
|
||||
* Eric Newberry <https://ericnewberry.com>
|
||||
* Junxiao Shi <https://cs.arizona.edu/~shijunxiao>
|
||||
* Jeff Thompson <https://remap.ucla.edu/jeff-thompson>
|
||||
* Yucheng Zhang <https://peterskycloud.wixsite.com/yzportfolio>
|
||||
* Italo Valcy S Brito <https://github.com/italovalcy>
|
||||
|
||||
|
||||
Technical Advisors
|
||||
-------------------
|
||||
|
||||
* Lan Wang <http://www.cs.memphis.edu/~lanwang>
|
||||
* Beichuan Zhang <http://cs.arizona.edu/~bzhang>
|
||||
|
||||
|
||||
The Mini-CCNx team
|
||||
-------------------
|
||||
|
||||
* Carlos Cabral <https://github.com/carlosmscabral>
|
||||
* Caio de Moraes Elias <https://github.com/ocaio>
|
||||
* Christian Esteve Rothenberg <http://www.dca.fee.unicamp.br/~chesteve>
|
||||
@@ -0,0 +1,52 @@
|
||||
Mininet Contributors
|
||||
|
||||
Mininet is an open source project and we gratefully acknowledge
|
||||
the many contributions to the project! If you have contributed
|
||||
code into the project and are not on this list, please let us know
|
||||
or send a pull request.
|
||||
|
||||
Contributors include:
|
||||
|
||||
Mininet Core Team (and alumni)
|
||||
|
||||
Bob Lantz
|
||||
Brandon Heller
|
||||
Nikhil Handigol
|
||||
Vimal Jeyakumar
|
||||
Brian O'Connor
|
||||
Cody Burkard
|
||||
|
||||
Additional Mininet Contributors
|
||||
|
||||
Tomasz Buchert
|
||||
Gustavo Pantuza Coelho Pinto
|
||||
Fernando Cappi
|
||||
Ryan Cox
|
||||
Shaun Crampton
|
||||
David Erickson
|
||||
Glen Gibb
|
||||
Andrew Ferguson
|
||||
Eder Leao Fernandes
|
||||
Gregory Gee
|
||||
Jon Hall
|
||||
Roan Huang
|
||||
Vitaly Ivanov
|
||||
Babis Kaidos
|
||||
Rich Lane
|
||||
Rémy Léone
|
||||
Zi Shen Lim
|
||||
David Mahler
|
||||
Murphy McCauley
|
||||
José Pedro Oliveira
|
||||
James Page
|
||||
Angad Singh
|
||||
Piyush Srivastava
|
||||
Ed Swierk
|
||||
Darshan Thaker
|
||||
Andreas Wundsam
|
||||
Isaku Yamahata
|
||||
Baohua Yang
|
||||
|
||||
Thanks also to everyone who has submitted issues and pull
|
||||
requests on github, and to our friendly mininet-discuss
|
||||
mailing list!
|
||||
-674
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
# Setup container with Ubuntu 22.04 image
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Set the working directory to /
|
||||
WORKDIR /
|
||||
|
||||
# expose ports for openvswitch-switch
|
||||
EXPOSE 6633 6653 6640
|
||||
|
||||
# Update container image
|
||||
RUN apt-get update -y && \
|
||||
apt-get install --no-install-recommends -y \
|
||||
lsb-release sudo \
|
||||
zip unzip wget git ca-certificates \
|
||||
curl iproute2 iputils-ping net-tools \
|
||||
python3 python3-pip \
|
||||
tcpdump vim x11-xserver-utils xterm && \
|
||||
update-ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
alias python=python3
|
||||
|
||||
COPY . /mini-ndn
|
||||
|
||||
RUN cd mini-ndn && \
|
||||
pip3 install -r requirements.txt && \
|
||||
./install.sh -y --source && \
|
||||
cd dl/mininet && make install && cd ../.. && \
|
||||
cd dl/mininet-wifi && make install && cd ../.. && \
|
||||
rm -rf dl && rm -rf /var/lib/apt/lists/* && cd /
|
||||
|
||||
COPY docker/ENTRYPOINT.sh /
|
||||
RUN chmod +x ENTRYPOINT.sh
|
||||
|
||||
# Change the working directory to /mini-ndn
|
||||
WORKDIR /mini-ndn
|
||||
|
||||
ENTRYPOINT ["/ENTRYPOINT.sh"]
|
||||
@@ -0,0 +1,179 @@
|
||||
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.2.1
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
pre-built VM image, and 2) native installation on Ubuntu. You can also
|
||||
easily create your own Mininet VM image (4).
|
||||
|
||||
(Other distributions may be supported in the future - if you would
|
||||
like to contribute an installation script, we would welcome it!)
|
||||
|
||||
1. Easiest "installation" - use our pre-built VM image!
|
||||
|
||||
The easiest way to get Mininet running is to start with one of our
|
||||
pre-built virtual machine images from <http://mininet.org/>
|
||||
|
||||
Boot up the VM image, log in, and follow the instructions on the
|
||||
Mininet web site.
|
||||
|
||||
One advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
|
||||
Although a single Mininet instance can simulate multiple networks
|
||||
with multiple controllers, only one Mininet instance may currently
|
||||
be run at a time, and Mininet requires root access in the machine
|
||||
it's running on. Therefore, if you have a multiuser system, you
|
||||
may wish to consider running Mininet in a VM.
|
||||
|
||||
2. Next-easiest option: use our Ubuntu package!
|
||||
|
||||
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
|
||||
12.10+:
|
||||
|
||||
sudo apt-get install mininet
|
||||
|
||||
Note: if you are upgrading from an older version of Mininet, make
|
||||
sure you remove the old OVS from `/usr/local`:
|
||||
|
||||
sudo rm /usr/local/bin/ovs*
|
||||
sudo rm /usr/local/sbin/ovs*
|
||||
|
||||
3. Native installation from source
|
||||
|
||||
3.1. Native installation from source on Ubuntu 12.04+
|
||||
|
||||
If you're reading this, you've probably already done so, but the
|
||||
command to download the Mininet source code is:
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
|
||||
Note that the above git command will check out the latest and greatest
|
||||
Mininet (which we recommend!) If you want to run the last tagged/released
|
||||
version of Mininet, you can look at the release tags using
|
||||
|
||||
cd mininet
|
||||
git tag
|
||||
|
||||
and then
|
||||
|
||||
git checkout <release tag>
|
||||
|
||||
where <release tag> is the release you want to check out.
|
||||
|
||||
If you are running Ubuntu, Debian, or Fedora, you may be able to use
|
||||
our handy `install.sh` script, which is in `mininet/util`.
|
||||
|
||||
*WARNING: USE AT YOUR OWN RISK!*
|
||||
|
||||
`install.sh` is a bit intrusive and may possibly damage your OS
|
||||
and/or home directory, by creating/modifying several directories
|
||||
such as `mininet`, `openflow`, `oftest`, `pox`, etc.. We recommend
|
||||
trying it in a VM before trying it on a system you use from day to day.
|
||||
|
||||
Although we hope it won't do anything completely terrible, you may
|
||||
want to look at the script before you run it, and you should make
|
||||
sure your system and home directory are backed up just in case!
|
||||
|
||||
To install Mininet itself, the OpenFlow reference implementation, and
|
||||
Open vSwitch, you may use:
|
||||
|
||||
mininet/util/install.sh -fnv
|
||||
|
||||
This should be reasonably quick, and the following command should
|
||||
work after the installation:
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
including POX, the OpenFlow WireShark dissector, the `oftest`
|
||||
framework, and other potentially useful software, you may use:
|
||||
|
||||
mininet/util/install.sh -a
|
||||
|
||||
This takes about 4 minutes on our test system.
|
||||
|
||||
You can change the directory where the dependencies are installed using
|
||||
the -s <directory> flag.
|
||||
|
||||
mininet/util/install.sh -s <directory> -a
|
||||
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
|
||||
As root execute the following operations:
|
||||
|
||||
* install git
|
||||
|
||||
yum install git
|
||||
|
||||
* create an user account (e.g. mininet) and add it to the wheel group
|
||||
|
||||
useradd [...] mininet
|
||||
usermod -a -G wheel mininet
|
||||
|
||||
* change the SElinux setting to permissive. It can be done
|
||||
temporarily with:
|
||||
|
||||
setenforce 0
|
||||
|
||||
then login with the new account (e.g. mininet) and do the following:
|
||||
|
||||
* clone the Mininet repository
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
|
||||
* install Mininet, the OpenFlow reference implementation, and
|
||||
Open vSwitch
|
||||
|
||||
mininet/util/install.sh -fnv
|
||||
|
||||
* enable and start openvswitch
|
||||
|
||||
sudo systemctl enable openvswitch
|
||||
sudo systemctl start openvswitch
|
||||
|
||||
* test the mininet installation
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||
|
||||
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
|
||||
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
|
||||
|
||||
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
|
||||
time install-mininet-vm.sh
|
||||
|
||||
Finally, verify that Mininet is installed and working in the VM:
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
5. Installation on other Linux distributions
|
||||
|
||||
Although we don't support other Linux distributions directly, it
|
||||
should be possible to install and run Mininet with some degree of
|
||||
manual effort.
|
||||
|
||||
In general, you must have:
|
||||
|
||||
* A Linux kernel compiled with network namespace support enabled
|
||||
|
||||
* An compatible software switch such as Open vSwitch or
|
||||
the Linux bridge.
|
||||
|
||||
* Python, `bash`, `ping`, `iperf`, etc.
|
||||
|
||||
* Root privileges (required for network device access)
|
||||
|
||||
We encourage contribution of patches to the `install.sh` script to
|
||||
support other Linux distributions.
|
||||
|
||||
|
||||
Good luck!
|
||||
|
||||
Mininet Team
|
||||
|
||||
---
|
||||
@@ -0,0 +1,33 @@
|
||||
Mininet 2.2.1 License
|
||||
|
||||
Copyright (c) 2013-2015 Open Networking Laboratory
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
|
||||
The Leland Stanford Junior University
|
||||
|
||||
Original authors: Bob Lantz and Brandon Heller
|
||||
|
||||
We are making Mininet available for public use and benefit with the
|
||||
expectation that others will use, modify and enhance the Software and
|
||||
contribute those enhancements back to the community. However, since we
|
||||
would like to make the Software available for broadest use, with as few
|
||||
restrictions as possible permission is hereby granted, free of charge, to
|
||||
any person obtaining a copy of this Software to deal in the Software
|
||||
under the copyrights without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
The name and trademarks of copyright holder(s) may NOT be used in
|
||||
advertising or publicity pertaining to the Software or any derivatives
|
||||
without specific, written prior permission.
|
||||
@@ -0,0 +1,73 @@
|
||||
MININET = mininet/*.py
|
||||
TEST = mininet/test/*.py
|
||||
EXAMPLES = mininet/examples/*.py
|
||||
MN = bin/mn
|
||||
BIN = $(MN)
|
||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||
MNEXEC = mnexec
|
||||
MANPAGES = mn.1 mnexec.1
|
||||
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
|
||||
BINDIR = /usr/bin
|
||||
MANDIR = /usr/share/man/man1
|
||||
DOCDIRS = doc/html doc/latex
|
||||
PDF = doc/latex/refman.pdf
|
||||
|
||||
CFLAGS += -Wall -Wextra
|
||||
|
||||
all: codecheck test
|
||||
|
||||
clean:
|
||||
rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS)
|
||||
|
||||
codecheck: $(PYSRC)
|
||||
-echo "Running code check"
|
||||
util/versioncheck.py
|
||||
pyflakes $(PYSRC)
|
||||
pylint --rcfile=.pylint $(PYSRC)
|
||||
# Exclude miniedit from pep8 checking for now
|
||||
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
|
||||
|
||||
errcheck: $(PYSRC)
|
||||
-echo "Running check for errors only"
|
||||
pyflakes $(PYSRC)
|
||||
pylint -E --rcfile=.pylint $(PYSRC)
|
||||
|
||||
test: $(MININET) $(TEST)
|
||||
-echo "Running tests"
|
||||
mininet/test/test_nets.py
|
||||
mininet/test/test_hifi.py
|
||||
|
||||
slowtest: $(MININET)
|
||||
-echo "Running slower tests (walkthrough, examples)"
|
||||
mininet/test/test_walkthrough.py -v
|
||||
mininet/examples/test/runner.py -v
|
||||
|
||||
mnexec: mnexec.c $(MN) mininet/net.py
|
||||
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
|
||||
|
||||
install: $(MNEXEC) $(MANPAGES)
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
python setup.py install
|
||||
|
||||
develop: $(MNEXEC) $(MANPAGES)
|
||||
# Perhaps we should link these as well
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
python setup.py develop
|
||||
|
||||
man: $(MANPAGES)
|
||||
|
||||
mn.1: $(MN)
|
||||
PYTHONPATH=. help2man -N -n "create a Mininet network." \
|
||||
--no-discard-stderr $< -o $@
|
||||
|
||||
mnexec.1: mnexec
|
||||
help2man -N -n "execution utility for Mininet." \
|
||||
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
|
||||
|
||||
.PHONY: doc
|
||||
|
||||
doc: man
|
||||
doxygen doc/doxygen.cfg
|
||||
make -C doc/latex
|
||||
@@ -1,32 +1,129 @@
|
||||
Mini-NDN
|
||||
========
|
||||
Mininet: Rapid Prototyping for Software Defined Networks
|
||||
========================================================
|
||||
|
||||
If you are new to the NDN community of software generally, read the
|
||||
[Contributor's Guide](https://github.com/named-data/.github/blob/master/CONTRIBUTING.md).
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
### What is Mini-NDN?
|
||||
Mininet 2.2.1
|
||||
|
||||
Mini-NDN is a lightweight networking emulation tool that enables testing, experimentation, and
|
||||
research on the NDN platform based on [Mininet](https://github.com/mininet/mininet).
|
||||
Mini-NDN uses the NDN libraries, NFD, NLSR, and tools released by the
|
||||
[NDN project](http://named-data.net/codebase/platform/) to emulate an NDN network on a single system.
|
||||
### What is Mininet?
|
||||
|
||||
Mini-NDN is open and free software licensed under the GPL 3.0 license. Mini-NDN is free to all
|
||||
users and developers. For more information about licensing details and limitations,
|
||||
please refer to [COPYING.md](COPYING.md).
|
||||
Mininet emulates a complete network of hosts, links, and switches
|
||||
on a single machine. To create a sample two-host, one-switch network,
|
||||
just run:
|
||||
|
||||
The first release of Mini-NDN is developed by members of the NSF-sponsored NDN project team.
|
||||
Mini-NDN is open to contribution from the public.
|
||||
For more details, please refer to [AUTHORS.rst](AUTHORS.rst).
|
||||
Bug reports and feedback are highly appreciated and can be made through our
|
||||
[Redmine site](http://redmine.named-data.net/projects/mini-ndn) and the
|
||||
[mini-ndn mailing list](http://www.lists.cs.ucla.edu/mailman/listinfo/mini-ndn).
|
||||
`sudo mn`
|
||||
|
||||
Mininet is useful for interactive development, testing, and demos,
|
||||
especially those using OpenFlow and SDN. OpenFlow-based network
|
||||
controllers prototyped in Mininet can usually be transferred to
|
||||
hardware with minimal changes for full line-rate execution.
|
||||
|
||||
### How does it work?
|
||||
|
||||
Mininet creates virtual networks using process-based virtualization
|
||||
and network namespaces - features that are available in recent Linux
|
||||
kernels. In Mininet, hosts are emulated as `bash` processes running in
|
||||
a network namespace, so any code that would normally run on a Linux
|
||||
server (like a web server or client program) should run just fine
|
||||
within a Mininet "Host". The Mininet "Host" will have its own private
|
||||
network interface and can only see its own processes. Switches in
|
||||
Mininet are software-based switches like Open vSwitch or the OpenFlow
|
||||
reference switch. Links are virtual ethernet pairs, which live in the
|
||||
Linux kernel and connect our emulated switches to emulated hosts
|
||||
(processes).
|
||||
|
||||
### Features
|
||||
|
||||
Mininet includes:
|
||||
|
||||
* A command-line launcher (`mn`) to instantiate networks.
|
||||
|
||||
* A handy Python API for creating networks of varying sizes and
|
||||
topologies.
|
||||
|
||||
* Examples (in the `examples/` directory) to help you get started.
|
||||
|
||||
* Full API documentation via Python `help()` docstrings, as well as
|
||||
the ability to generate PDF/HTML documentation with `make doc`.
|
||||
|
||||
* Parametrized topologies (`Topo` subclasses) using the Mininet
|
||||
object. For example, a tree network may be created with the
|
||||
command:
|
||||
|
||||
`mn --topo tree,depth=2,fanout=3`
|
||||
|
||||
* A command-line interface (`CLI` class) which provides useful
|
||||
diagnostic commands (like `iperf` and `ping`), as well as the
|
||||
ability to run a command to a node. For example,
|
||||
|
||||
`mininet> h11 ifconfig -a`
|
||||
|
||||
tells host h11 to run the command `ifconfig -a`
|
||||
|
||||
* A "cleanup" command to get rid of junk (interfaces, processes, files
|
||||
in /tmp, etc.) which might be left around by Mininet or Linux. Try
|
||||
this if things stop working!
|
||||
|
||||
`mn -c`
|
||||
|
||||
### New features in this release
|
||||
|
||||
This is primarily a performance improvement and bug fix release.
|
||||
|
||||
- Batch startup has been implemented for Open vSwitch, improving
|
||||
startup performance.
|
||||
|
||||
- OVS patch links have been implemented via OVSLink and --link ovs
|
||||
|
||||
Warning! These links have *serious limitations* compared to
|
||||
virtual Ethernet pairs: they are not attached to real Linux
|
||||
interfaces so you cannot use tcpdump or wireshark with them;
|
||||
they also cannot be used in long chains - we don't recommend more
|
||||
than 64 OVSLinks, for example --linear,64. However, they can offer
|
||||
significantly better performance than veth pairs, for certain
|
||||
configurations.
|
||||
|
||||
- You can now easily install Mininet on a Raspberry Pi ;-)
|
||||
|
||||
- Additional information for this release and previous releases
|
||||
may be found in the release notes on docs.mininet.org
|
||||
|
||||
### Installation
|
||||
|
||||
See `INSTALL` for installation instructions and details.
|
||||
|
||||
### Documentation
|
||||
|
||||
Please refer to http://minindn.memphis.edu/ or [docs/index.rst](docs/index.rst) for installation, usage, and other documentation.
|
||||
The documentation can be built using:
|
||||
In addition to the API documentation (`make doc`), much useful
|
||||
information, including a Mininet walkthrough and an introduction
|
||||
to the Python API, is available on the
|
||||
[Mininet Web Site](http://mininet.org).
|
||||
There is also a wiki which you are encouraged to read and to
|
||||
contribute to, particularly the Frequently Asked Questions (FAQ.)
|
||||
|
||||
./docs/build.sh
|
||||
### Support
|
||||
|
||||
and is available under `docs/_build/html`.
|
||||
Mininet is community-supported. We encourage you to join the
|
||||
Mininet mailing list, `mininet-discuss` at:
|
||||
|
||||
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
|
||||
|
||||
### Join Us
|
||||
|
||||
Thanks again to all of the Mininet contributors!
|
||||
|
||||
Mininet is an open source project and is currently hosted
|
||||
at <https://github.com/mininet>. You are encouraged to download
|
||||
the code, examine it, modify it, and submit bug reports, bug fixes,
|
||||
feature requests, new features and other issues and pull requests.
|
||||
Thanks to everyone who has contributed code to the Mininet project
|
||||
(see CONTRIBUTORS for more info!) It is because of everyone's
|
||||
hard work that Mininet continues to grow and improve.
|
||||
|
||||
### Enjoy Mininet
|
||||
|
||||
Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
|
||||
Bob Lantz
|
||||
Mininet Core Team
|
||||
@@ -0,0 +1,406 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Mininet runner
|
||||
author: Brandon Heller (brandonh@stanford.edu)
|
||||
|
||||
To see options:
|
||||
sudo mn -h
|
||||
|
||||
Example to pull custom params (topo, switch, etc.) from a file:
|
||||
sudo mn --custom ~/mininet/custom/custom_example.py
|
||||
"""
|
||||
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Fix setuptools' evil madness, and open up (more?) security holes
|
||||
if 'PYTHONPATH' in os.environ:
|
||||
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||
|
||||
from mininet.clean import cleanup
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, LEVELS, info, debug, warn, error
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
Ryu, NOX, RemoteController, findController,
|
||||
DefaultController, NullController,
|
||||
UserSwitch, OVSSwitch, OVSBridge,
|
||||
IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink, OVSLink
|
||||
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
|
||||
SingleSwitchReversedTopo, MinimalTopo )
|
||||
from mininet.topolib import TreeTopo, TorusTopo
|
||||
from mininet.util import customClass, specialClass, splitArgs
|
||||
from mininet.util import buildTopo
|
||||
|
||||
from functools import partial
|
||||
|
||||
# Experimental! cluster edition prototype
|
||||
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
|
||||
RemoteOVSSwitch, RemoteLink,
|
||||
SwitchBinPlacer, RandomPlacer,
|
||||
ClusterCleanup )
|
||||
from mininet.examples.clustercli import ClusterCLI
|
||||
|
||||
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
|
||||
|
||||
# built in topologies, created only when run
|
||||
TOPODEF = 'minimal'
|
||||
TOPOS = { 'minimal': MinimalTopo,
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo,
|
||||
'torus': TorusTopo }
|
||||
|
||||
SWITCHDEF = 'default'
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
'ovs': OVSSwitch,
|
||||
'ovsbr' : OVSBridge,
|
||||
# Keep ovsk for compatibility with 2.0
|
||||
'ovsk': OVSSwitch,
|
||||
'ivs': IVSSwitch,
|
||||
'lxbr': LinuxBridge,
|
||||
'default': OVSSwitch }
|
||||
|
||||
HOSTDEF = 'proc'
|
||||
HOSTS = { 'proc': Host,
|
||||
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
|
||||
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
|
||||
|
||||
CONTROLLERDEF = 'default'
|
||||
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'ryu': Ryu,
|
||||
'default': DefaultController, # Note: replaced below
|
||||
'none': NullController }
|
||||
|
||||
LINKDEF = 'default'
|
||||
LINKS = { 'default': Link,
|
||||
'tc': TCLink,
|
||||
'ovs': OVSLink }
|
||||
|
||||
|
||||
# optional tests to run
|
||||
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||
'none' ]
|
||||
|
||||
ALTSPELLING = { 'pingall': 'pingAll',
|
||||
'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp',
|
||||
'iperfUDP': 'iperfUdp' }
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
"""Convenience function to add choices dicts to OptionParser.
|
||||
opts: OptionParser instance
|
||||
choicesDict: dictionary of valid choices, must include default
|
||||
default: default choice key
|
||||
name: long option name
|
||||
kwargs: additional arguments to add_option"""
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
helpList = [ '%s=%s' % ( k, v.__name__ )
|
||||
for k, v in choicesDict.items() ]
|
||||
helpStr += ' ' + ( ' '.join( helpList ) )
|
||||
params = dict( type='string', default=default, help=helpStr )
|
||||
params.update( **kwargs )
|
||||
opts.add_option( '--' + name, **params )
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
print "%s" % VERSION
|
||||
exit()
|
||||
|
||||
|
||||
class MininetRunner( object ):
|
||||
"Build, setup, and run Mininet."
|
||||
|
||||
def __init__( self ):
|
||||
"Init."
|
||||
self.options = None
|
||||
self.args = None # May be used someday for more CLI scripts
|
||||
self.validate = None
|
||||
|
||||
self.parseArgs()
|
||||
self.setup()
|
||||
self.begin()
|
||||
|
||||
def custom( self, _option, _opt_str, value, _parser ):
|
||||
"""Parse custom file and add params.
|
||||
option: option e.g. --custom
|
||||
opt_str: option string e.g. --custom
|
||||
value: the value the follows the option
|
||||
parser: option parser instance"""
|
||||
files = []
|
||||
if os.path.isfile( value ):
|
||||
# Accept any single file (including those with commas)
|
||||
files.append( value )
|
||||
else:
|
||||
# Accept a comma-separated list of filenames
|
||||
files += value.split(',')
|
||||
|
||||
for fileName in files:
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
def setCustom( self, name, value ):
|
||||
"Set custom parameters for MininetRunner."
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links' ):
|
||||
# Update dictionaries
|
||||
param = name.upper()
|
||||
globals()[ param ].update( value )
|
||||
elif name == 'validate':
|
||||
# Add custom validate function
|
||||
self.validate = value
|
||||
else:
|
||||
# Add or modify global variable or class
|
||||
globals()[ name ] = value
|
||||
|
||||
def setNat( self, _option, opt_str, value, parser ):
|
||||
"Set NAT option(s)"
|
||||
assert self # satisfy pylint
|
||||
parser.values.nat = True
|
||||
# first arg, first char != '-'
|
||||
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-':
|
||||
value = parser.rargs.pop( 0 )
|
||||
_, args, kwargs = splitArgs( opt_str + ',' + value )
|
||||
parser.values.nat_args = args
|
||||
parser.values.nat_kwargs = kwargs
|
||||
else:
|
||||
parser.values.nat_args = []
|
||||
parser.values.nat_kwargs = {}
|
||||
|
||||
def parseArgs( self ):
|
||||
"""Parse command-line args and return options object.
|
||||
returns: opts parse options dict"""
|
||||
|
||||
desc = ( "The %prog utility creates Mininet network from the\n"
|
||||
"command line. It can create parametrized topologies,\n"
|
||||
"invoke the Mininet CLI, and run tests." )
|
||||
|
||||
usage = ( '%prog [options]\n'
|
||||
'(type %prog -h for details)' )
|
||||
|
||||
opts = OptionParser( description=desc, usage=usage )
|
||||
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
||||
addDictOption( opts, CONTROLLERS, [], 'controller', action='append' )
|
||||
addDictOption( opts, LINKS, LINKDEF, 'link' )
|
||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||
|
||||
opts.add_option( '--clean', '-c', action='store_true',
|
||||
default=False, help='clean and exit' )
|
||||
opts.add_option( '--custom', action='callback',
|
||||
callback=self.custom,
|
||||
type='string',
|
||||
help='read custom classes or params from .py file(s)'
|
||||
)
|
||||
|
||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||
default=TESTS[ 0 ],
|
||||
help='|'.join( TESTS ) )
|
||||
opts.add_option( '--xterms', '-x', action='store_true',
|
||||
default=False, help='spawn xterms for each node' )
|
||||
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
|
||||
help='base IP address for hosts' )
|
||||
opts.add_option( '--mac', action='store_true',
|
||||
default=False, help='automatically set host MACs' )
|
||||
opts.add_option( '--arp', action='store_true',
|
||||
default=False, help='set all-pairs ARP entries' )
|
||||
opts.add_option( '--verbosity', '-v', type='choice',
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6634,
|
||||
help='base port for passive switch listening' )
|
||||
opts.add_option( '--nolistenport', action='store_true',
|
||||
default=False, help="don't use passive listening " +
|
||||
"port")
|
||||
opts.add_option( '--pre', type='string', default=None,
|
||||
help='CLI script to run before tests' )
|
||||
opts.add_option( '--post', type='string', default=None,
|
||||
help='CLI script to run after tests' )
|
||||
opts.add_option( '--pin', action='store_true',
|
||||
default=False, help="pin hosts to CPU cores "
|
||||
"(requires --host cfs or --host rt)" )
|
||||
opts.add_option( '--nat', action='callback', callback=self.setNat,
|
||||
help="[option=val...] adds a NAT to the topology that"
|
||||
" connects Mininet hosts to the physical network."
|
||||
" Warning: This may route any traffic on the machine"
|
||||
" that uses Mininet's"
|
||||
" IP subnet into the Mininet network."
|
||||
" If you need to change"
|
||||
" Mininet's IP subnet, see the --ipbase option." )
|
||||
opts.add_option( '--version', action='callback', callback=version,
|
||||
help='prints the version and exits' )
|
||||
opts.add_option( '--cluster', type='string', default=None,
|
||||
metavar='server1,server2...',
|
||||
help=( 'run on multiple servers (experimental!)' ) )
|
||||
opts.add_option( '--placement', type='choice',
|
||||
choices=PLACEMENT.keys(), default='block',
|
||||
metavar='block|random',
|
||||
help=( 'node placement for --cluster '
|
||||
'(experimental!) ' ) )
|
||||
|
||||
self.options, self.args = opts.parse_args()
|
||||
|
||||
# We don't accept extra arguments after the options
|
||||
if self.args:
|
||||
opts.print_help()
|
||||
exit()
|
||||
|
||||
def setup( self ):
|
||||
"Setup and validate environment."
|
||||
|
||||
# set logging verbosity
|
||||
if LEVELS[self.options.verbosity] > LEVELS['output']:
|
||||
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
'output!\n'
|
||||
'Please restart Mininet with -v [debug, info, output].'
|
||||
% self.options.verbosity )
|
||||
lg.setLogLevel( self.options.verbosity )
|
||||
|
||||
# Maybe we'll reorganize this someday...
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
|
||||
def begin( self ):
|
||||
"Create and run mininet."
|
||||
|
||||
if self.options.cluster:
|
||||
servers = self.options.cluster.split( ',' )
|
||||
for server in servers:
|
||||
ClusterCleanup.add( server )
|
||||
|
||||
if self.options.clean:
|
||||
cleanup()
|
||||
exit()
|
||||
|
||||
start = time.time()
|
||||
|
||||
if not self.options.controller:
|
||||
# Update default based on available controllers
|
||||
CONTROLLERS[ 'default' ] = findController()
|
||||
self.options.controller = [ 'default' ]
|
||||
if not CONTROLLERS[ 'default' ]:
|
||||
self.options.controller = [ 'none' ]
|
||||
if self.options.switch == 'default':
|
||||
info( '*** No default OpenFlow controller found '
|
||||
'for default switch!\n' )
|
||||
info( '*** Falling back to OVS Bridge\n' )
|
||||
self.options.switch = 'ovsbr'
|
||||
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
raise Exception( "Could not find a default controller "
|
||||
"for switch %s" %
|
||||
self.options.switch )
|
||||
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customClass( SWITCHES, self.options.switch )
|
||||
host = customClass( HOSTS, self.options.host )
|
||||
controller = [ customClass( CONTROLLERS, c )
|
||||
for c in self.options.controller ]
|
||||
link = customClass( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( self.options )
|
||||
|
||||
ipBase = self.options.ipbase
|
||||
xterms = self.options.xterms
|
||||
mac = self.options.mac
|
||||
arp = self.options.arp
|
||||
pin = self.options.pin
|
||||
listenPort = None
|
||||
if not self.options.nolistenport:
|
||||
listenPort = self.options.listenport
|
||||
|
||||
# Handle inNamespace, cluster options
|
||||
inNamespace = self.options.innamespace
|
||||
cluster = self.options.cluster
|
||||
if inNamespace and cluster:
|
||||
print "Please specify --innamespace OR --cluster"
|
||||
exit()
|
||||
Net = MininetWithControlNet if inNamespace else Mininet
|
||||
cli = ClusterCLI if cluster else CLI
|
||||
if cluster:
|
||||
warn( '*** WARNING: Experimental cluster mode!\n'
|
||||
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
|
||||
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
|
||||
Net = partial( MininetCluster, servers=servers,
|
||||
placement=PLACEMENT[ self.options.placement ] )
|
||||
|
||||
mn = Net( topo=topo,
|
||||
switch=switch, host=host, controller=controller,
|
||||
link=link,
|
||||
ipBase=ipBase,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, autoPinCpus=pin,
|
||||
listenPort=listenPort )
|
||||
|
||||
if self.options.ensure_value( 'nat', False ):
|
||||
nat = mn.addNAT( *self.options.nat_args,
|
||||
**self.options.nat_kwargs )
|
||||
nat.configDefault()
|
||||
|
||||
if self.options.pre:
|
||||
cli( mn, script=self.options.pre )
|
||||
|
||||
test = self.options.test
|
||||
test = ALTSPELLING.get( test, test )
|
||||
|
||||
mn.start()
|
||||
|
||||
if test == 'none':
|
||||
pass
|
||||
elif test == 'all':
|
||||
mn.waitConnected()
|
||||
mn.start()
|
||||
mn.ping()
|
||||
mn.iperf()
|
||||
elif test == 'cli':
|
||||
cli( mn )
|
||||
elif test != 'build':
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )()
|
||||
|
||||
if self.options.post:
|
||||
cli( mn, script=self.options.post )
|
||||
|
||||
mn.stop()
|
||||
|
||||
elapsed = float( time.time() - start )
|
||||
info( 'completed in %0.3f seconds\n' % elapsed )
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
MininetRunner()
|
||||
except KeyboardInterrupt:
|
||||
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
|
||||
cleanup()
|
||||
except Exception:
|
||||
# Print exception
|
||||
type_, val_, trace_ = sys.exc_info()
|
||||
errorMsg = ( "-"*80 + "\n" +
|
||||
"Caught exception. Cleaning up...\n\n" +
|
||||
"%s: %s\n" % ( type_.__name__, val_ ) +
|
||||
"-"*80 + "\n" )
|
||||
error( errorMsg )
|
||||
# Print stack trace to debug log
|
||||
import traceback
|
||||
stackTrace = traceback.format_exc()
|
||||
debug( stackTrace + "\n" )
|
||||
cleanup()
|
||||
@@ -0,0 +1,6 @@
|
||||
This directory should hold configuration files for custom mininets.
|
||||
|
||||
See custom_example.py, which loads the default minimal topology. The advantage of defining a mininet in a separate file is that you then use the --custom option in mn to run the CLI or specific tests with it.
|
||||
|
||||
To start up a mininet with the provided custom topology, do:
|
||||
sudo mn --custom custom_example.py --topo mytopo
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Custom topology example
|
||||
|
||||
Two directly connected switches plus a host for each switch:
|
||||
|
||||
host --- switch --- switch --- host
|
||||
|
||||
Adding the 'topos' dict with a key/value pair to generate our newly defined
|
||||
topology enables one to pass in '--topo=mytopo' from the command line.
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
|
||||
class MyTopo( Topo ):
|
||||
"Simple topology example."
|
||||
|
||||
def __init__( self ):
|
||||
"Create custom topo."
|
||||
|
||||
# Initialize topology
|
||||
Topo.__init__( self )
|
||||
|
||||
# Add hosts and switches
|
||||
leftHost = self.addHost( 'h1' )
|
||||
rightHost = self.addHost( 'h2' )
|
||||
leftSwitch = self.addSwitch( 's3' )
|
||||
rightSwitch = self.addSwitch( 's4' )
|
||||
|
||||
# Add links
|
||||
self.addLink( leftHost, leftSwitch )
|
||||
self.addLink( leftSwitch, rightSwitch )
|
||||
self.addLink( rightSwitch, rightHost )
|
||||
|
||||
|
||||
topos = { 'mytopo': ( lambda: MyTopo() ) }
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
mininet (2.1.0-0ubuntu1) saucy; urgency=low
|
||||
|
||||
* Add 2.1.0 final packaging
|
||||
|
||||
-- Bob Lantz <rlantz@cs.stanford.edu> Wed, 18 Sep 2013 22:43:47 -0700
|
||||
|
||||
mininet (2.1.0~rc1-0ubuntu1) saucy; urgency=low
|
||||
|
||||
* New upstream release candidate:
|
||||
- d/control: Drop dependency on python-networkx, add iperf, socat
|
||||
and cgroup-bin to Depends.
|
||||
|
||||
-- James Page <james.page@ubuntu.com> Wed, 28 Aug 2013 10:10:20 +0100
|
||||
|
||||
mininet (2.0.0-0ubuntu1) raring; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- James Page <james.page@ubuntu.com> Wed, 19 Dec 2012 15:48:01 +0000
|
||||
|
||||
mininet (2.0.0~rc1-0ubuntu1) quantal; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
* Update copyright to match upstream release
|
||||
* Fix this message
|
||||
|
||||
-- Bob Lantz <rlantz@cs.stanford.edu> Sun, 18 Nov 2012 00:15:09 -0800
|
||||
|
||||
mininet (2.0.0~d4-0ubuntu1) quantal; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Bob Lantz <rlantz@cs.stanford.edu> Tue, 07 Aug 2012 14:11:27 -0700
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
9
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
Source: mininet
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||
XSBC-Original-Maintainer: Bob Lantz <rlantz@cs.stanford.edu>
|
||||
Standards-Version: 3.9.3
|
||||
Build-Depends:
|
||||
debhelper (>= 9~),
|
||||
help2man,
|
||||
python-dev,
|
||||
python-pkg-resources,
|
||||
python-setuptools
|
||||
Homepage: http://openflow.org/mininet
|
||||
|
||||
Package: mininet
|
||||
Architecture: any
|
||||
Depends:
|
||||
openvswitch-switch,
|
||||
telnet,
|
||||
socat,
|
||||
iperf,
|
||||
cgroup-bin,
|
||||
${misc:Depends},
|
||||
${python:Depends},
|
||||
${shlibs:Depends}
|
||||
Recommends: openvswitch-controller
|
||||
Description: Process-based network emulator
|
||||
Mininet is a network emulator which uses lightweight
|
||||
virtualization to create virtual networks for rapid
|
||||
prototyping of Software-Defined Network (SDN) designs
|
||||
using OpenFlow.
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
|
||||
Upstream-Name: mininet
|
||||
Source: https://github.com/mininet/mininet
|
||||
|
||||
Files: *
|
||||
Copyright: 2012-2013 Open Networking Laboratory,
|
||||
2009-2012 Bob Lantz,
|
||||
2009-2012 The Board of Trustees of the Leland Stanford Junior
|
||||
University
|
||||
License:
|
||||
Original authors: Bob Lantz and Brandon Heller
|
||||
.
|
||||
We are making Mininet available for public use and benefit with the
|
||||
expectation that others will use, modify and enhance the Software and
|
||||
contribute those enhancements back to the community. However, since we
|
||||
would like to make the Software available for broadest use, with as few
|
||||
restrictions as possible permission is hereby granted, free of charge, to
|
||||
any person obtaining a copy of this Software to deal in the Software
|
||||
under the copyrights without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
.
|
||||
The name and trademarks of copyright holder(s) may NOT be used in
|
||||
advertising or publicity pertaining to the Software or any derivatives
|
||||
without specific, written prior permission.
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
README.md
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
examples/*
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
mnexec /usr/bin
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
*.1
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=python_distutils --with=python2
|
||||
|
||||
override_dh_auto_build:
|
||||
make man
|
||||
make mnexec
|
||||
dh_auto_build
|
||||
|
||||
get-orig-source:
|
||||
uscan --force-download --rename
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
3.0 (quilt)
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
version=3
|
||||
opts=filenamemangle=s/(.*)\/archive/$1/,uversionmangle=s/([abdr].*)\.tar\.gz/~$1/ \
|
||||
https://github.com/mininet/mininet/tags .*/archive\/(\d.*\.tar\.gz)
|
||||
File renamed without changes.
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# set python3 alias, but needs permanent fix in image directly
|
||||
alias python=python3
|
||||
|
||||
service openvswitch-switch start
|
||||
ovs-vsctl set-manager ptcp:6640
|
||||
|
||||
bash
|
||||
|
||||
service openvswitch-switch stop
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
[comments]: The original author of Mini-NDN docker is Md Ashiqur Rahman (marahman@email.arizona.edu)
|
||||
|
||||
## Running Mini-NDN inside Docker
|
||||
|
||||
You can use the nightly build from GitHub package registry
|
||||
```bash
|
||||
docker run -m 4g --cpus=4 -it --privileged \
|
||||
-v /lib/modules:/lib/modules \
|
||||
ghcr.io/named-data/mini-ndn:master bash
|
||||
```
|
||||
|
||||
## Building your own image
|
||||
|
||||
The Dockerfile can be used directly to `build` an image from scratch.
|
||||
|
||||
* Build with `Dockerfile`:
|
||||
* Clone the repository and type.
|
||||
```bash
|
||||
docker build -t minindn .
|
||||
```
|
||||
* You can then access the container through shell with,
|
||||
```bash
|
||||
docker run -m 4g --cpus=4 -it --privileged \
|
||||
-v /lib/modules:/lib/modules \
|
||||
minindn bin/bash
|
||||
```
|
||||
|
||||
### Notes:
|
||||
|
||||
* Memory (-m), CPU (--cpus) are recommended by Mini-NDN.
|
||||
* `--privileged` is mandatory for underlying [Mininet](http://mininet.org/) to utilize virtual switch
|
||||
* Root directory on `run` is `/mini-ndn` containing the installation and examples.
|
||||
* GUI may not work for now due to docker and xterm setup issues and is independent from Mini-NDN.
|
||||
If you intend to run the GUI, pass `-e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix` to the `docker run` command.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: ../AUTHORS.rst
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
# -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set -eo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
PIP='python3 -m pip'
|
||||
|
||||
for PIPPKG in sphinx sphinx_rtd_theme; do
|
||||
if ! $PIP show $PIPPKG >/dev/null; then
|
||||
sudo $PIP install $PIPPKG
|
||||
fi
|
||||
done
|
||||
|
||||
make clean html
|
||||
@@ -1,61 +0,0 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
from datetime import datetime
|
||||
from minindn import __version__
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Mini-NDN'
|
||||
copyright = '2015-{}, Mini-NDN. This research is partially supported by NSF.'.format(datetime.now().year)
|
||||
author = 'Mini-NDN'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = __version__
|
||||
version = __version__
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Need to specify the line below if custom conf.py
|
||||
# https://github.com/readthedocs/readthedocs.org/issues/2569
|
||||
# https://stackoverflow.com/questions/56336234/build-fail-sphinx-error-contents-rst-not-found
|
||||
master_doc = 'index'
|
||||
@@ -1,274 +0,0 @@
|
||||
Experiment
|
||||
==========
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Mini-NDN uses a configuration file describing the topology and its parameters to setup a network.
|
||||
|
||||
The [nodes] section:
|
||||
|
||||
At the bare minimum, the node section describes the nodes present in the
|
||||
topology.
|
||||
|
||||
::
|
||||
|
||||
[nodes]
|
||||
a: key1=value1 key2=value2
|
||||
b: key1=value1
|
||||
|
||||
Any key and value passed here is accessible in Mini-NDN as:
|
||||
|
||||
::
|
||||
|
||||
ndn = Minindn(...)
|
||||
value = ndn.net.hosts[0].params['params'].get('key1', "defaultValue")
|
||||
|
||||
One can specify log levels for each node's NFD and NLSR using this key-value system:
|
||||
|
||||
::
|
||||
|
||||
[nodes]
|
||||
a: nfd-log-level=DEBUG nlsr-log-level=DEBUG
|
||||
b: nfd-log-level=INFO
|
||||
|
||||
To specify a log level for certain modules of NFD, the following line can be added to `nfd.py`:
|
||||
|
||||
::
|
||||
|
||||
node.cmd('infoedit -f {} -s log.Forwarder -v {}'.format(self.confFile, 'INFO'))
|
||||
|
||||
This will turn on FORWARDER logging to INFO for all nodes.
|
||||
|
||||
.. Todo: Add switch section
|
||||
|
||||
The [links] section:
|
||||
|
||||
The links section describes the links in the topology.
|
||||
|
||||
::
|
||||
|
||||
e.g.)
|
||||
|
||||
[links]
|
||||
a:b delay=10ms
|
||||
|
||||
This would create a link between a and b. 'b:a' would also result in the
|
||||
same. The following parameters can be configured for a node:
|
||||
|
||||
- delay : Delay parameter is a required parameter which defines the
|
||||
delay of the link (1-1000ms)
|
||||
|
||||
- bw : Bandwidth of a link (<1-1000> Mbps)
|
||||
|
||||
- loss : Percentage of packet loss (<1-100>)
|
||||
|
||||
Example configuration file
|
||||
|
||||
::
|
||||
|
||||
[nodes]
|
||||
a:
|
||||
b:
|
||||
[links]
|
||||
a:b delay=10ms bw=100
|
||||
|
||||
See ``ndn_utils/topologies`` for more sample files
|
||||
|
||||
Sample
|
||||
------
|
||||
|
||||
Sample experiment may written as follows:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.appmanager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.routing_helper import IPRoutingHelper
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
# Can pass a custom parser, custom topology, or any Mininet params here
|
||||
ndn = Minindn()
|
||||
|
||||
ndn.start()
|
||||
|
||||
# IP reachability if needed
|
||||
# IPRoutingHelper.calcAllRoutes(ndn.net)
|
||||
# info("IP routes configured, start ping\n")
|
||||
# ndn.net.pingAll()
|
||||
|
||||
# Start apps with AppManager which registers a clean up function with ndn
|
||||
info('Starting NFD on nodes\n')
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
info('Starting NLSR on nodes\n')
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
|
||||
# or can not start NLSRs with some delay in between:
|
||||
# nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
# for host in ndn.net.hosts:
|
||||
# nlsrs.startOnNode(host)
|
||||
# time.sleep(30)
|
||||
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
# Calls the clean up functions registered via AppManager
|
||||
ndn.stop()
|
||||
|
||||
Users may look at how the NFD and NLSR applications are written as a sub class of Application
|
||||
in the ``minindn/apps`` folder. Or users may choose to directly run their application on nodes
|
||||
such as ndnpingserver is run in ``minindn/helpers/experiment.py``.
|
||||
|
||||
**Note:** A certain log-level can be set-up for all the NFD or NLSR nodes at once by passing it as an argument during the startup.
|
||||
|
||||
``nfds = AppManager(self.ndn, self.ndn.net.hosts, Nfd, logLevel='DEBUG')`` (same for NLSR)
|
||||
|
||||
Execution
|
||||
---------
|
||||
|
||||
To run Mini-NDN with the default topology,
|
||||
``ndn_utils/topologies/default-topology.conf``, type:
|
||||
|
||||
::
|
||||
|
||||
sudo python examples/minindn.py
|
||||
|
||||
To run Mini-NDN with a topology file, provide the filename as the first
|
||||
argument:
|
||||
|
||||
::
|
||||
|
||||
sudo python examples/minindn.py my-topology.conf
|
||||
|
||||
After Mini-NDN is installed, users can run examples from anywhere with python directly as follows:
|
||||
|
||||
::
|
||||
|
||||
sudo python /path/to/myexample.py
|
||||
|
||||
The user no longer needs to create an experiment in the old Mini-NDN way, then install it to the system before executing it via the minindn binary. The new examples can be separate from the Mini-NDN folder if the core is not being modified.
|
||||
|
||||
CLI Interface
|
||||
_____________
|
||||
|
||||
During set up, the list of nodes in the network will be listed as they
|
||||
are initialized:
|
||||
|
||||
::
|
||||
|
||||
*** Adding hosts:
|
||||
a b c d
|
||||
|
||||
After set up, the command-line interface (CLI) will display a prompt.
|
||||
|
||||
::
|
||||
|
||||
mini-ndn>
|
||||
|
||||
To interact with a node, first type the node's name and then the command
|
||||
to be executed:
|
||||
|
||||
::
|
||||
|
||||
mini-ndn> a echo "Hello, world!"
|
||||
Hello, world!
|
||||
|
||||
To see the status of the forwarder on the node:
|
||||
|
||||
::
|
||||
|
||||
mini-ndn> a nfdc status report
|
||||
|
||||
To see the status of routing on the node:
|
||||
|
||||
::
|
||||
|
||||
mini-ndn> a nlsrc status
|
||||
|
||||
To exit Mini-NDN, type ``quit`` in the CLI or use ``ctrl + D``:
|
||||
|
||||
::
|
||||
|
||||
mini-ndn> quit
|
||||
|
||||
``Ctrl + C`` is used to quit an application run in the foreground of the command line.
|
||||
|
||||
For a more in depth explanation of the CLI, please see the `Mininet
|
||||
Walkthrough <http://mininet.org/walkthrough/>`__.
|
||||
|
||||
To run NDN commands from the outside the command line user can also open a new terminal
|
||||
and export the HOME folder of a node ``export HOME=/tmp/minindn/a && cd ~``
|
||||
|
||||
Working Directory Structure
|
||||
---------------------------
|
||||
|
||||
Currently Mini-NDN uses /tmp/minindn as the working directory if not
|
||||
specified otherwise by using the option --work-dir.
|
||||
|
||||
Each node is given a HOME directory under /tmp/minindn/<node-name> where
|
||||
<node-name> is the name of the node specified in the [nodes] section of
|
||||
the conf file.
|
||||
|
||||
NFD
|
||||
___
|
||||
|
||||
- NFD conf file is stored at ``/tmp/minindn/<node-name>/nfd.conf``
|
||||
|
||||
- NFD log file is stored at ``/tmp/minindn/<node-name>/log/nfd.log``
|
||||
|
||||
- ``.ndn`` folder is stored at ``/tmp/minindn/<node-name>/.ndn``
|
||||
|
||||
NLSR
|
||||
____
|
||||
|
||||
- NLSR conf file is stored at ``/tmp/minindn/<node-name>/nlsr.conf``
|
||||
- NLSR log file is stored at ``/tmp/minindn/<node-name>/log/nlsr.log``
|
||||
|
||||
When security is enabled, NLSR security certificates are stored in:
|
||||
``/tmp/minindn/<node-name>/security`` Note that no NLSR publishes the root
|
||||
certificate, Mini-NDN installs root.cert in security folder for each
|
||||
NLSR.
|
||||
|
||||
While a host's NLSR neighbors are by default populated by adjacent nodes in wired scenarios,
|
||||
for those running NLSR on wifi stations it is required that you specify "neighbor" faces
|
||||
manually. The framework for this is provided either via a dictionary object or through
|
||||
additional sections in topology files, and may also be used for wired experiments.
|
||||
See an example of a topo of this sort in ``mini-ndn/topologies/wifi/nlsr_wifi_example.conf``.
|
||||
NLSR faces to be created can be manually specified from topology files in a ``[faces]``
|
||||
section, with the format ``nodeA:nodeB [cost=X]``. You should then call the ``setupFaces()``
|
||||
method of an initialized Mini-NDN object to get a dictionary based on this parse in the format
|
||||
``faceA:[(faceB, cost), (faceC, cost),...]``, which can finally be passed to the NLSR
|
||||
helper via the faceDict parameter. An example experiment using this methodology is located
|
||||
at ``mini-ndn/examples/wifi/nlsr_wifi.py``. Note that the aforementioned dict can also be
|
||||
created manually in the previously established format.
|
||||
|
||||
Routing Options
|
||||
----------------
|
||||
|
||||
Link State Routing (NLSR)
|
||||
_________________________
|
||||
By default, Mini-NDN uses `NLSR <https://github.com/named-data/NLSR>`__ for route management i.e route computation, route installation and so on. Additionally, the command line utility ``nlsrc`` can be used to advertise and withdraw prefixes and route status.
|
||||
|
||||
|
||||
NDN Routing Helper
|
||||
____________________
|
||||
Computes link-state or hyperbolic route/s from a given minindn topology and installs them in the FIB. The major benefit of the routing helper is to eliminate the overhead of NLSR when using larger topology. See ``examples/static_routing_experiment.py`` on how to use the helper class.
|
||||
|
||||
**IMPORTANT:** NLSR and NDN Routing Helper are mutually exclusive, meaning you can only use one at a time, not both.
|
||||
|
||||
**Note:** The current version of ``ndn_routing_helper`` is still in the experimental phase. It doesn't support node or link failure and runtime prefix advertisement/withdrawal. If you find any bug please report `here <https://redmine.named-data.net/projects/mini-ndn>`__ or contact the :doc:`authors <authors>`.
|
||||
|
||||
IP Routing Helper
|
||||
____________________
|
||||
|
||||
The routing helper allows to run IP-based evaluations with Mini-NDN. It configures static IP routes to all nodes, which means that all nodes can reach all other nodes in the network
|
||||
reachable, even when relaying is required. Please see ``examples/ip_rounting_experiment.py`` for a simple example.
|
||||
@@ -1,48 +0,0 @@
|
||||
FAQ
|
||||
=========
|
||||
|
||||
* ``How does Mini-NDN work?``
|
||||
|
||||
Mini-NDN's principles of operation most heavily rely on the underlying Mininet code it relies on.
|
||||
Mininet uses a combination of limited containerization via network namespaces (which give processes
|
||||
isolated interfaces and routing tables) and emulated ethernet connections via veth connections.
|
||||
In practical terms, Mini-NDN ensures that processes running on distinct nodes will run seperately
|
||||
and without interfering with each other.
|
||||
|
||||
* ``How does Mini-NDN apply link loss/delay/etc.?``
|
||||
|
||||
Mini-NDN relies on Mininet's code, which in turn uses the Linux tc utility on a stations' virtualized
|
||||
interfaces to apply configurations known as qdiscs to these links. Note that these will only be applied
|
||||
on egress packets from a station where it's applied.
|
||||
For more information on qdiscs and tc, view the information `here <http://wiki.linuxwall.info/doku.php/en%3aressources%3adossiers%3anetworking%3atraffic_control>`_.
|
||||
|
||||
* ``Why use Mini-NDN rather than a simulator such as ndnSIM?``
|
||||
|
||||
Mini-NDN is easier and faster to use because, rather than serving as a mathematical model of a network,
|
||||
it is instead running real NDN code on a real Linux kernel. This also means it's quite useful for testing code changes, as it can more accurately test the interaction of software componenents.
|
||||
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Criteria | Mini-NDN | ndnSIM |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Based On | Mininet | ns-3 |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Language | Python | C++ |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Library/Forwarder/Applications | Use system binaries (free to use any compatible versions) | Integrated (fixed release version) |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Application language | C++ (ndn-cxx), CCL (ndn-cpp, PyNDN, ndn-js, jNDN) | C++ (ndn-cxx) |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Simulation size | Medium - Large (cluster edition in development) | Large (can be parallelized using MPI) |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Simulation time | Real time | Quick (depending on size/memory) |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Porting real applications | Drop in | Changes required |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Interactivity | Can interact directly with NFD, NLSR or Apps | Can show stats while running |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Logs | May need to manually setup to collect | Available with tracer |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Post processing scripts | Not available, users need to write their own | Available to use to process the logs |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
| Other | Not yet supported (Wifi in development) | WiFi, LTE, etc available from ns-3 |
|
||||
+--------------------------------+-----------------------------------------------------------+-----------------------------------------+
|
||||
@@ -1,10 +0,0 @@
|
||||
Past NDN Hackathon projects
|
||||
===========================
|
||||
|
||||
- 1st NDN Hackathon: `NFD integration test <http://ndncomm.github.io/mini-ndn/>`_.
|
||||
- 2st NDN Hackathon: `Mini-NDN metrics <https://github.com/2nd-ndn-hackathon/mini-ndn-metrics>`_.
|
||||
- 3rd NDN Hackathon: `Mini-NDN cluster <https://github.com/3rd-ndn-hackathon/mini-NDN-cluster>`_.
|
||||
- 4th NDN Hackathon: `Mini-NDN wifi <https://github.com/4th-ndn-hackathon/Mini-NDN-Wi-Fi>`_.
|
||||
- 7th NDN Hackathon: `Mini-NDN documentation <https://github.com/7th-ndn-hackathon/mini-ndn-documentation>`_.
|
||||
- 11th NDN Hackathon: `Mini-NDN improvements <https://11th-ndn-hackathon.named-data.net/hacks.html#8-mini-ndn-improvements>`_.
|
||||
- 12th NDN Hackathon: `Mini-NDN improvements and Refactoring <https://12th-ndn-hackathon.named-data.net/hacks.html#7-mini-ndn-improvements>`_.
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
Howtos
|
||||
======
|
||||
|
||||
Connect Mini-NDN nodes to an outside network
|
||||
---------------------------------------------
|
||||
|
||||
Mini-NDN nodes can be connected to an outside network indirectly by
|
||||
running NFD on the local machine:
|
||||
|
||||
::
|
||||
|
||||
(Mini-NDN node) ------ (NFD running on the host machine where Mini-NDN is running) ------- (External Network)
|
||||
|
||||
Add a node in root namespace
|
||||
____________________________
|
||||
|
||||
For this simple example, we can use a single node topology with node 'a'
|
||||
|
||||
If we want node 'a' to connect to the host machine, we need to add a
|
||||
"root" node which has a link with node "a."
|
||||
|
||||
Then the following code can be used:
|
||||
|
||||
.. code:: python
|
||||
|
||||
topo = Topo()
|
||||
root = topo.addHost('root', inNamespace=False)
|
||||
a = topo.addHost('a')
|
||||
topo.addLink(root, a, delay='10ms')
|
||||
|
||||
ndn = Minindn(topo=topo)
|
||||
|
||||
...
|
||||
|
||||
Configuration
|
||||
_____________
|
||||
|
||||
Run Mini-NDN with the above code and issue ifconfig on the local
|
||||
machine to confirm the addition of the interface. You should be able to
|
||||
locate "root-eth0":
|
||||
|
||||
::
|
||||
|
||||
root-eth0 Link encap:Ethernet HWaddr 3e:eb:77:d2:6f:1f
|
||||
inet addr:1.0.0.9 Bcast:1.0.0.11 Mask:255.255.255.252
|
||||
inet6 addr: fe80::3ceb:77ff:fed2:6f1f/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:34 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:33 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:2667 (2.6 KB) TX bytes:2797 (2.7 KB)
|
||||
|
||||
To make the IP address associated with this interface persistent, add
|
||||
the following line to /etc/network/interfaces and reboot your machine:
|
||||
|
||||
::
|
||||
|
||||
iface root-eth0 inet manual
|
||||
|
||||
Check connection
|
||||
________________
|
||||
|
||||
After rebooting, run Mini-NDN and issue the following command:
|
||||
|
||||
::
|
||||
|
||||
mini-ndn>net
|
||||
a a-eth0:b-eth0 a-eth1:c-eth0 a-eth2:root-eth0
|
||||
|
||||
Node "a" is connected to "root-eth0". Now issue "ifconfig a-eth2" on
|
||||
node "a":
|
||||
|
||||
::
|
||||
|
||||
mini-ndn>a ifconfig a-eth2
|
||||
a-eth2 Link encap:Ethernet HWaddr fa:76:d4:86:d3:ba
|
||||
inet addr:1.0.0.10 Bcast:1.0.0.11 Mask:255.255.255.252
|
||||
|
||||
As learned from the previous step, the IP address of root-eth0 is
|
||||
1.0.0.9.
|
||||
|
||||
::
|
||||
|
||||
mini-ndn>a ping 1.0.0.9
|
||||
PING 1.0.0.9 (1.0.0.9) 56(84) bytes of data.
|
||||
64 bytes from 1.0.0.9: icmp_seq=1 ttl=64 time=0.137 ms
|
||||
64 bytes from 1.0.0.9: icmp_seq=2 ttl=64 time=0.123 ms
|
||||
|
||||
The host machine will also be able to ping node "a":
|
||||
|
||||
::
|
||||
|
||||
VirtualBox:~$ ping 1.0.0.10
|
||||
PING 1.0.0.10 (1.0.0.10) 56(84) bytes of data.
|
||||
64 bytes from 1.0.0.10: icmp_seq=1 ttl=64 time=0.086 ms
|
||||
|
||||
Run NFD on local machine and register route
|
||||
___________________________________________
|
||||
|
||||
Start NFD on the local machine by using:
|
||||
|
||||
::
|
||||
|
||||
sudo nfd
|
||||
|
||||
The "nfd-start" script cannot be used, since the script allows only one
|
||||
instance of NFD at a time. The NFD processes running on the Mini-NDN
|
||||
nodes will prevent the "nfd-start" script from working.
|
||||
|
||||
Now, using "nfdc register", we can register a route from node "a" in
|
||||
Mini-NDN to the NFD process on the host machine and from the host
|
||||
machine to an external machine.
|
||||
|
||||
Also, if the local machine has a public IP, Mini-NDN nodes can be
|
||||
reached via external machines.
|
||||
|
||||
Generate NDN testbed topology
|
||||
___________________________________________
|
||||
|
||||
Run the following install.sh command
|
||||
|
||||
::
|
||||
|
||||
python3 util/testbed_topo_generator.py
|
||||
|
||||
This will place a "testbed.conf" file in the topologies subdirectory,
|
||||
where it can be referenced as desired. To update the topology, simply
|
||||
rerun this command.
|
||||
@@ -1,30 +0,0 @@
|
||||
.. Mini-NDN documentation master file, created by
|
||||
sphinx-quickstart on Mon Sep 23 11:15:54 2019.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Mini-NDN: A Mininet-based NDN emulator
|
||||
======================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents
|
||||
|
||||
introduction
|
||||
install
|
||||
experiment
|
||||
howtos
|
||||
release-notes
|
||||
faq
|
||||
hackathon
|
||||
videos
|
||||
|
||||
Helpful Links
|
||||
-------------
|
||||
|
||||
* `NDN Website <http://named-data.net/>`_
|
||||
* `NDN Contributor's Guide <https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md>`_
|
||||
* `Mininet Documentation <http://mininet.org/>`_
|
||||
* `Mini-NDN redmine <https://redmine.named-data.net/projects/mini-ndn>`_
|
||||
* `Mailing list <http://www.lists.cs.ucla.edu/mailman/listinfo/mini-ndn>`_
|
||||
* :doc:`Mini-NDN Team <authors>`
|
||||
@@ -1,184 +0,0 @@
|
||||
Install
|
||||
=======
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
Mini-NDN is tested on the following Linux distributions:
|
||||
|
||||
- Ubuntu 20.04 (recommended)
|
||||
- Ubuntu 22.04
|
||||
- Debian 11 (WiFi scenario does not work)
|
||||
- Fedora 33 (WiFi scenario does not work)
|
||||
|
||||
You must have sudo privileges to install and run Mini-NDN.
|
||||
|
||||
Using Vagrantfile
|
||||
-----------------
|
||||
|
||||
With Vagrant installed, simply do ``vagrant up`` which will bring up an Ubuntu 18.04 virtual machine
|
||||
and install Mini-NDN and all its dependencies on it. Please make sure to tweak the CPU core count
|
||||
(default 4 cores) and RAM (default 4GB) according to your needs before doing vagrant up. Mini-NDN
|
||||
can be found in /home/vagrant/mini-ndn which is a symlink to /vagrant if Vagrantfile was used from within mini-ndn cloned on the host. Otherwise it is an actual clone of mini-ndn.
|
||||
|
||||
Using install.sh
|
||||
----------------
|
||||
|
||||
Mini-NDN has the following dependencies:
|
||||
|
||||
- `NDN Forwarding Daemon (NFD) <https://named-data.net/doc/NFD/>`_
|
||||
- `Named Data Link State Routing (NLSR) <https://named-data.net/doc/NLSR/>`_
|
||||
- `NDN Essential Tools (ndn-tools) <https://github.com/named-data/ndn-tools>`_
|
||||
- `NDN Traffic Generator <https://github.com/named-data/ndn-traffic-generator>`_
|
||||
- `infoedit <https://github.com/NDN-Routing/infoedit>`_
|
||||
- `Mininet <http://mininet.org/>`_
|
||||
- `Mininet-WiFi <https://mininet-wifi.github.io/>`_ (optional)
|
||||
|
||||
To install Mini-NDN and its dependencies, clone this repository and run:
|
||||
|
||||
::
|
||||
|
||||
./install.sh
|
||||
|
||||
The script accepts various command line flags.
|
||||
Some notable flags are:
|
||||
|
||||
- ``-y`` skips interactive confirmation before installation.
|
||||
- ``--ppa`` prefers installing NDN software from `named-data PPA <https://launchpad.net/~named-data/+archive/ubuntu/ppa>`_.
|
||||
This shortens installation time by downloading binary packages, but is only available on Ubuntu.
|
||||
- ``--source`` prefers installing NDN software from source code.
|
||||
|
||||
IMPORTANT: For now, Mininet-WiFi only works with ``--source`` installation because the current NFD release (0.7.1) doesn't
|
||||
incorporate `issue 5155 <https://redmine.named-data.net/issues/5155>`, a required patch for WiFi module to work properly.
|
||||
With the next NFD release, Mininet-WiFi will work with both ``source`` and ``ppa``. Alternatively, you can
|
||||
checkout (at your own risk) a third-party source "`Use NFD nightly with Mini-NDN <https://yoursunny.com/t/2021/NFD-nightly-minindn/>`", which provides
|
||||
NFD-nightly version and contains all the necessary patches.
|
||||
|
||||
- ``--dummy-keychain`` patches ndn-cxx to use an in-memory dummy KeyChain, which reduces CPU overhead
|
||||
and allows you to scale up Mini-NDN experiments. Large Mini-NDN experiments would run significantly
|
||||
faster after applying this patch. However, your experiments cannot use any NDN security related
|
||||
features (signatures, verifier, access control, etc).
|
||||
- ``--no-wifi`` skips Mininet-WiFi dependency.
|
||||
Currently Mininet-WiFi only works on Ubuntu, so that you must specify this option when installing on other distros.
|
||||
|
||||
You can see all command line flags by running:
|
||||
|
||||
::
|
||||
|
||||
./install.sh -h
|
||||
|
||||
The script uses ``setup.py develop`` to point the system install of Python packages to the codebase
|
||||
directory. Therefore, you can modify ``mininet``, ``mininet-wifi``, and ``mini-ndn``, and the
|
||||
changes will be reflected immediately.
|
||||
|
||||
If NDN software is installed from source code (not PPA), the code is downloaded to ``dl`` directory
|
||||
under your ``mini-ndn`` clone. If you modify the source code, you need to manually recompile and
|
||||
reinstall the software (``./waf && sudo ./waf install``).
|
||||
|
||||
|
||||
Installing Dependencies
|
||||
-----------------------
|
||||
|
||||
This section outlines how to install dependnecies manually.
|
||||
If you used ``install.sh``, you do not need to perform these steps.
|
||||
|
||||
Mininet
|
||||
_______
|
||||
|
||||
Mini-NDN is based on Mininet. To install Mininet:
|
||||
|
||||
::
|
||||
|
||||
git clone --depth 1 https://github.com/mininet/mininet.git
|
||||
|
||||
After Mininet source is on your system, run the following command to
|
||||
install Mininet core dependencies and Open vSwitch:
|
||||
|
||||
::
|
||||
|
||||
./util/install.sh -nv
|
||||
|
||||
To check if Mininet is working correctly, run this test:
|
||||
|
||||
::
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
This will print out a series of statements that show the test setup and
|
||||
the results of the test. Look for ``Results:`` two-thirds of the way
|
||||
down where it will indicate the percentage of dropped packets. Your
|
||||
results should show "0% dropped (2/2 received)".
|
||||
|
||||
NOTE: Mini-NDN, while providing a high level of emulation of hosts,
|
||||
requires programs to be installed onto your computer. It will not work
|
||||
if they are not installed. If you do not want NDN software installed
|
||||
onto your computer, you can use a virtual machine, which can be quite
|
||||
simply set up with the provided Vagrantfile.
|
||||
|
||||
NDN dependencies
|
||||
________________
|
||||
|
||||
Each node in Mini-NDN will run the official implementation of NDN
|
||||
installed on your system. The following dependencies are needed:
|
||||
|
||||
Mini-NDN uses NFD, NLSR, and ndn-tools.
|
||||
|
||||
- To install NFD: https://named-data.net/doc/NFD/current/INSTALL.html
|
||||
- To install NLSR: https://named-data.net/doc/NLSR/current/INSTALL.html
|
||||
- To install ndn-tools: https://github.com/named-data/ndn-tools
|
||||
|
||||
.. warning::
|
||||
Please do not try to install NDN software from both the source (GitHub) and PPA (apt).
|
||||
It will not work in most cases! If you used ./install.sh -a in the past but now want
|
||||
to use apt, please run ``sudo ./waf uninstall`` in all the NDN projects before proceeding
|
||||
with apt. Similarly, remove from apt if switching to source.
|
||||
|
||||
Please see the :ref:`scaling-note <scaling-note>` to learn about disabling
|
||||
security for better scalability.
|
||||
|
||||
Note that all three of these can be installed from the Named Data PPA.
|
||||
Instructions for setting it up can be found in the NFD installation
|
||||
instructions. Note that PPA and installs from source **cannot** be
|
||||
mixed. You must completely remove PPA installs from the system if switching
|
||||
to source and vice-versa.
|
||||
|
||||
For PPA installs, if you are using a custom nfd.conf file in an experiment, you should
|
||||
place it in /usr/local/etc/ndn/ rather than /etc/ndn/. This is to avoid
|
||||
a bug from the default configuration file for the PPA, which is
|
||||
incompatible with Mini-NDN.
|
||||
|
||||
Infoedit
|
||||
________
|
||||
|
||||
Infoedit is used to edit configuration files for NFD and NLSR.
|
||||
To install infoedit:
|
||||
|
||||
::
|
||||
|
||||
git clone --depth 1 https://github.com/NDN-Routing/infoedit
|
||||
cd infoedit
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Verification
|
||||
------------
|
||||
|
||||
You can execute the following example to bring up the Mini-NDN command line
|
||||
with NFD and NLSR running on each node:
|
||||
|
||||
::
|
||||
|
||||
sudo python examples/mnndn.py
|
||||
|
||||
You can use these steps to run the sample pingall experiment:
|
||||
|
||||
1. Issue the command: ``sudo python examples/nlsr/pingall.py``
|
||||
2. When the ``mini-ndn>`` CLI prompt appears, the experiment has
|
||||
finished. On the Mini-NDN CLI, issue the command ``exit`` to exit the
|
||||
experiment.
|
||||
3. Issue the command:
|
||||
``grep -c content /tmp/minindn/*/ping-data/*.txt``. Each file should
|
||||
report a count of 50.
|
||||
4. Issue the command:
|
||||
``grep -c timeout /tmp/minindn/*/ping-data/*.txt``. Each file should
|
||||
report a count of 0.
|
||||
@@ -1,40 +0,0 @@
|
||||
Introduction
|
||||
=================
|
||||
|
||||
If you are new to the NDN community of software generally, read the
|
||||
`Contributor's Guide <https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md>`_.
|
||||
|
||||
What is Mini-NDN?
|
||||
-----------------
|
||||
|
||||
Mini-NDN is a lightweight networking emulation tool that enables testing, experimentation, and
|
||||
research on the NDN platform. It was initially based on `Mini-CCNx <https://github.com/chesteve/mn-ccnx>`_ which was a fork of `Mininet <https://github.com/mininet/mininet>`_. Mini-NDN uses the NDN libraries, NFD, NLSR, and tools released by the `NDN project <http://named-data.net/codebase/platform/>`_ to emulate an NDN network on a single system.
|
||||
|
||||
The first release of Mini-NDN is developed by members of the NSF-sponsored NDN project team.
|
||||
Mini-NDN is open to contribution from the public.
|
||||
|
||||
.. image:: minindnnet.svg
|
||||
|
||||
License
|
||||
_______
|
||||
|
||||
Mini-NDN is open and free software licensed under the GPL 3.0 license. Mini-NDN is free to all
|
||||
users and developers. For more information about licensing details and limitations,
|
||||
please refer to COPYING.md.
|
||||
|
||||
Feedback/Mailing List
|
||||
_____________________
|
||||
|
||||
Bug reports and feedback are highly appreciated and can be made through our
|
||||
`Redmine site <http://redmine.named-data.net/projects/mini-ndn>`_ and the
|
||||
`mini-ndn mailing list <http://www.lists.cs.ucla.edu/mailman/listinfo/mini-ndn>`_.
|
||||
|
||||
Video
|
||||
_____
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div id="video-container" class="col-md-6 ">
|
||||
<p>Mini-NDN (content maybe outdated)</p>
|
||||
<iframe width="600" height="345" src="https://www.youtube.com/embed/UxHPqaUwefg" frameborder="0" allowfullscreen=""></iframe>
|
||||
</div>
|
||||
@@ -1,126 +0,0 @@
|
||||
<svg width="692" height="380" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<metadata id="metadata7">image/svg+xml</metadata>
|
||||
<g>
|
||||
<title>background</title>
|
||||
<rect fill="none" id="canvas_background" height="382" width="694" y="-1" x="-1"/>
|
||||
</g>
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<g stroke="null" id="g3790">
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" id="rect2985" width="680.84682" height="350.443392" x="4.962804" y="5.773119" rx="0.324652"/>
|
||||
<path stroke="#000000" fill="none" stroke-miterlimit="4" d="m4,175.828501l681.727736,1.357954" id="path3770"/>
|
||||
</g>
|
||||
<rect stroke="#000000" fill="#e3dedb" stroke-width="0.99538" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" rx="2.988095" y="176.111777" x="5.140841" height="180.524655" width="680.312936" id="rect3813"/>
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-width="1.602133" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dasharray="4.80639973, 4.80639973" stroke-dashoffset="0" ry="8.582684" rx="7.476771" y="44.78072" x="55.492192" height="195.219243" width="148.366914" id="rect3764"/>
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-width="1.638042" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dasharray="4.9141253, 4.9141253" stroke-dashoffset="0" id="rect3772" width="155.115252" height="195.190135" x="270.220868" y="46.153231" rx="7.816845" ry="8.581405"/>
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-width="1.602133" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dasharray="4.80639973, 4.80639973" stroke-dashoffset="0" ry="8.582684" rx="7.476771" y="45.459696" x="489.86568" height="195.219243" width="148.366914" id="rect3774"/>
|
||||
<text transform="matrix(1.0192718802397343,0,0,0.9115536073383187,-2.8313337784806336,-12.183034089773187) " font-size="16.005802px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3776" y="392.709737" x="551.171304" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="normal" y="398.194877" x="552.152396" id="tspan3778">Kernel Space</tspan>
|
||||
</text>
|
||||
<text transform="matrix(1.0524848569125234,0,0,0.8827879284304422,-52.30106698198968,-77.5201889102731) " font-size="14.180006px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="601.466013" y="108.524983" id="text3782">
|
||||
<tspan stroke="null" font-weight="normal" id="tspan3784" x="602.416145" y="114.188857">User Space</tspan>
|
||||
</text>
|
||||
<path stroke="#000000" fill="none" stroke-width="1px" id="path3796" d="m488.56554,176.773764c1.782236,0.480113 1.527628,0.360085 1.527628,0.360085"/>
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-width="0.649807" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" rx="0.838801" y="202.851471" x="84.255138" height="26.434283" width="94.537572" id="rect3815"/>
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-width="0.792138" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" id="rect3817" width="131.339764" height="28.275376" x="282.077514" y="201.912133" rx="1.165334"/>
|
||||
<path stroke="#000000" fill="none" stroke-width="0.865499px" id="path3823" d="m347.74739,201.086282l0,28.351986"/>
|
||||
<text transform="matrix(0.7776670647796514,0,0,1.19475416519067,-52.30106724263901,-77.52018902784596) " font-size="12.623897px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="434.634023" y="248.956571" id="text3835">
|
||||
<tspan stroke="null" font-size="15.579311px" font-weight="bold" font-family="Monospace" x="435.91992" y="253.141532" id="tspan3837">n2-eth0</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9031157255303414,0,0,1.0287949657006918,-2.8313337284492768,-13.069358400177762) " font-size="13.163442px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="110.835751" y="225.210496" id="text3863">
|
||||
<tspan stroke="null" font-size="16.245171px" font-weight="bold" font-family="Monospace" x="111.943029" y="230.070551" id="tspan3865">n1-eth0</tspan>
|
||||
</text>
|
||||
<path stroke="#000000" fill="none" stroke-width="0.992521px" id="path3887" d="m131.263954,229.882774l0,67.221296l176.555867,-0.960307l1.337531,-65.300682"/>
|
||||
<rect stroke="#000000" fill="#ffffff" stroke-width="0.676601" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" id="rect3891" width="95.679394" height="28.317195" x="515.346399" y="202.85306" rx="0.848932"/>
|
||||
<path stroke="#000000" fill="none" stroke-width="0.992521px" d="m383.833251,230.842992l0,67.221296l176.555867,-0.960307l1.337531,-65.300682" id="path3897"/>
|
||||
<text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="164.136782" y="409.888383" id="text3907">
|
||||
<tspan stroke="null" id="tspan3915" x="165.159464" y="415.15046">isolated point to point link</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3919" y="409.573373" x="429.378262" xml:space="preserve">
|
||||
<tspan stroke="null" y="414.83545" x="430.400944" id="tspan3921">isolated point to point link</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3923" y="428.078233" x="194.615642" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="bold" y="433.34031" x="195.638324" id="tspan3925">(eg: 1Mbps, 10ms)</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3927" y="409.888383" x="164.136782" xml:space="preserve">
|
||||
<tspan stroke="null" y="415.15046" x="165.159464" id="tspan3929">Isolated point to point link</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="429.378262" y="409.573373" id="text3931">
|
||||
<tspan stroke="null" id="tspan3933" x="430.400944" y="414.83545">Isolated point to point link</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.877827228897152,0,0,1.0584326041598764,-53.30106731891522,-78.4233265445007) " font-size="13.706223px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3935" y="282.545218" x="661.482605" xml:space="preserve">
|
||||
<tspan stroke="null" font-size="16.915024px" font-weight="bold" font-family="Monospace" id="tspan3937" y="287.269185" x="662.621781">n3-eth0</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.7776670647796514,0,0,1.19475416519067,-52.30106724263901,-77.52018902784596) " font-size="12.623897px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3939" y="248.297391" x="520.751053" xml:space="preserve">
|
||||
<tspan stroke="null" font-size="15.579311px" font-weight="bold" font-family="Monospace" id="tspan3941" y="252.482352" x="522.03695">n2-eth1</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.8745385143393076,0,0,1.0624128521471832,-2.8313338199928846,-14.069358364146652) " font-size="16.205975px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3943" y="80.648481" x="141.655977" xml:space="preserve">
|
||||
<tspan stroke="null" font-size="20px" font-weight="bold" font-family="Monospace" id="tspan3945" y="85.354749" x="142.799437">n1</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.8745385143393076,0,0,1.0624128521471832,-2.8313338199928846,-14.069358364146652) " font-size="16.205975px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="385.430517" y="78.840861" id="text3955">
|
||||
<tspan stroke="null" font-size="20px" font-weight="bold" font-family="Monospace" x="386.573977" y="83.547129" id="tspan3957">n2</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.8745385143393076,0,0,1.0624128521471832,-2.8313338199928846,-14.069358364146652) " font-size="16.205975px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3959" y="79.181231" x="632.310457" xml:space="preserve">
|
||||
<tspan stroke="null" font-size="20px" font-weight="bold" font-family="Monospace" id="tspan3961" y="83.887499" x="633.453917">n3</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="125.371536" y="171.060971" id="text3967">
|
||||
<tspan stroke="null" font-weight="normal" id="tspan3969" x="126.421253" y="176.187525">(NDN Container)</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3979" y="170.076461" x="352.011746" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="normal" y="175.203015" x="353.061463" id="tspan3981">(NDN Container)</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="580.077396" y="169.091931" id="text3983">
|
||||
<tspan stroke="null" font-weight="normal" id="tspan3985" x="581.127113" y="174.218485">(NDN Container)</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3987" y="193.704961" x="123.946116" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="bold" y="198.831515" x="124.995833" id="tspan3989">NFD</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="140.791686" y="208.664491" id="text3991">
|
||||
<tspan stroke="null" font-weight="normal" id="tspan3993" x="141.841403" y="213.791045">n1.sock</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="123.946116" y="225.286191" id="text3995">
|
||||
<tspan stroke="null" font-weight="bold" id="tspan3997" x="124.995833" y="230.412745">NLSR</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3999" y="240.245731" x="138.385176" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="normal" y="245.372285" x="139.434893" id="tspan4001">%C1.Router/n1</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="352.011746" y="191.735901" id="text4003">
|
||||
<tspan stroke="null" font-weight="bold" id="tspan4005" x="353.061463" y="196.862455">NFD</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4007" y="206.695431" x="366.450896" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="normal" y="211.821985" x="367.500613" id="tspan4009">n2.sock</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4011" y="223.317141" x="352.011746" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="bold" y="228.443695" x="353.061463" id="tspan4013">NLSR</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="366.450896" y="238.276671" id="text4015">
|
||||
<tspan stroke="null" font-weight="normal" id="tspan4017" x="367.500613" y="243.403225">%C1.Router/n2</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4019" y="190.751371" x="582.928226" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="bold" y="195.877925" x="583.977943" id="tspan4021">NFD</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="599.773686" y="205.710911" id="text4023">
|
||||
<tspan stroke="null" font-weight="normal" id="tspan4025" x="600.823403" y="210.837465">n3.sock</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="582.928226" y="222.332611" id="text4027">
|
||||
<tspan stroke="null" font-weight="bold" id="tspan4029" x="583.977943" y="227.459165">NLSR</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4031" y="237.292141" x="597.367186" xml:space="preserve">
|
||||
<tspan stroke="null" font-weight="normal" y="242.418695" x="598.416903" id="tspan4033">%C1.Router/n3</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4035" y="102.475413" x="322.701992" xml:space="preserve">
|
||||
<tspan stroke="null" y="107.73749" x="323.724674" id="tspan4037">Isolated NDN nodes</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.935021961689298,0,0,0.9936889310882339,-2.9798247609055646,-12.086171479984756) " font-size="11.279781px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="75.549398" y="206.131281" id="text4039">
|
||||
<tspan stroke="null" id="tspan4041" x="76.618892" y="211.163037">Private network space</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.935021961689298,0,0,0.9936889310882339,-2.9798247609055646,-12.086171479984756) " font-size="11.279781px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4043" y="206.946991" x="308.474828" xml:space="preserve">
|
||||
<tspan stroke="null" y="211.978747" x="309.544322" id="tspan4045">Private network space</tspan>
|
||||
</text>
|
||||
<text transform="matrix(0.935021961689298,0,0,0.9936889310882339,-2.9798247609055646,-12.086171479984756) " font-size="11.279781px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="540.174348" y="206.131281" id="text4047">
|
||||
<tspan stroke="null" id="tspan4049" x="541.243842" y="211.163037">Private network space</tspan>
|
||||
</text>
|
||||
<text xml:space="preserve" text-anchor="start" font-family="Helvetica,Arial,sans-serif" font-size="15px" id="svg_1" y="377" x="203.5" stroke-width="null" stroke="null" fill="#333">Figure: Relationship between Minindn and Mininet</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,286 +0,0 @@
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
Mini-NDN version 0.6.0 (Major changes since version 0.5.0)
|
||||
----------------------------------------------------------
|
||||
|
||||
**Breaking Changes**:
|
||||
|
||||
- Rewrite install script (`issue: 4630 <https://redmine.named-data.net/issues/4630>`__)
|
||||
|
||||
- Set dependency versions: PPA, git repository & commit
|
||||
- Separate download and build+install steps
|
||||
- Don't reinstall package if it's already installed
|
||||
- More details `here <https://github.com/named-data/mini-ndn/blob/master/docs/install.rst>`__
|
||||
|
||||
- `Note: <https://redmine.named-data.net/issues/5161>`__ We have dropped support for python 2, the latest Mini-NDN requires at least python 3.0
|
||||
|
||||
**New features**:
|
||||
|
||||
- Update Mini-NDN codebase with Mini-NDN-Wifi code (`issue: 4858 <https://redmine.named-data.net/issues/4858>`__)
|
||||
|
||||
- Provide pre-built Mini-NDN Vagrant box and Docker container
|
||||
|
||||
- Added several new examples:
|
||||
|
||||
- consumer/producer
|
||||
- ndnping
|
||||
- traffic generator
|
||||
- catchunks/putchunks
|
||||
|
||||
- Allow for creation of net object without topology (`issue: 5162 <https://redmine.named-data.net/issues/5162>`__)
|
||||
|
||||
**Improvements and Bug Fixes**:
|
||||
|
||||
- Support running NDN applications on mixed topologies (`issue: 5160 <https://redmine.named-data.net/issues/5160>`__)
|
||||
|
||||
- Support route addition using face-id in `Nfdc` helper (`issue: 5130 <https://redmine.named-data.net/issues/5130>`__)
|
||||
|
||||
- Add wrapper for `ndnpingserver` and fix passing the Mininet host object as a prefix on ndnpingclient
|
||||
|
||||
- Show status of route calculation in `NdnRoutingHelper`
|
||||
|
||||
- Incorporate changes of `NDNPing` Class (wrapper of pingserver and pingclient) in the examples
|
||||
|
||||
- Support simple topology files with no additional parameters
|
||||
|
||||
|
||||
Mini-NDN version 0.5.0 (Major changes since version 0.4.0)
|
||||
----------------------------------------------------------
|
||||
|
||||
**Breaking Changes**:
|
||||
|
||||
- `Mini-NDN re-design <https://redmine.named-data.net/issues/5062>`__: simple and robust design with better quality, control, and more consistency with Mininet
|
||||
|
||||
**New features**:
|
||||
|
||||
- Add a script to generate up-to-date NDN testbed topologies for Mini-NDN
|
||||
|
||||
- Add Mini-NDN utility application for PCAP logging
|
||||
|
||||
- Add NDN routing helper to compute centralized LS and HR routes
|
||||
|
||||
- Add routing helper to allow IP communication in experiments
|
||||
|
||||
- Add startup experiments for NLSR and current testbed topology
|
||||
|
||||
- Move the NDNPing wrapper method to a helper class
|
||||
|
||||
- Create a helper class to provide a wrapper around ``nfdc``
|
||||
|
||||
**Improvements and Bug Fixes**:
|
||||
|
||||
- Change workDir and resultDir to be class attribute
|
||||
|
||||
- Quiet apt install for Vagrant
|
||||
|
||||
- Fix route computation bug in ``ndn_routing_helper``
|
||||
|
||||
- Fix overwriting of existing prefixes in ``ndn_routing_helper``
|
||||
|
||||
- Move log files to resultDir after evaluation finishes
|
||||
|
||||
- Check for duplicate HR coordinates in the topology file
|
||||
|
||||
- Check PSync integration and add a tests case for it
|
||||
|
||||
- Bug fixes in nfdc and experiments
|
||||
|
||||
- Added functionality to check Mini-NDN dependencies
|
||||
|
||||
- Parser fix to avoid an infinite loop
|
||||
|
||||
- Allow use of NFD and NLSR PPA with Mini-NDN
|
||||
|
||||
- Remove arbitrary arguments in favor of parsing arguments from experiment files
|
||||
|
||||
- Auto-complete command-line arguments
|
||||
|
||||
- Add option to set CS size
|
||||
|
||||
- Adjust to use ndn-cxx logging
|
||||
|
||||
|
||||
Mini-NDN version 0.4.0 (changes since version 0.3.0)
|
||||
----------------------------------------------------
|
||||
|
||||
Release date: January 10, 2018
|
||||
|
||||
**New features**:
|
||||
|
||||
- Use SIGQUIT to quit Mini-NDN, SIGINT to kill programs
|
||||
|
||||
- Use Infoedit to edit NFD and NLSR configuration files
|
||||
|
||||
- Use nlsr.conf installed in the system
|
||||
|
||||
- Provide a Vagrantfile to setup Mini-NDN and NDN
|
||||
|
||||
- Provide option to disable NLSR
|
||||
|
||||
- Provide an option to run NLSR in dry-run mode
|
||||
|
||||
- Add option to specify whether to use TCP or UDP face in nlsr.conf
|
||||
|
||||
- Add option to specify arbitrary arguments to use in experiments
|
||||
|
||||
- Include a single option to install Mini-NDN and all the dependencies
|
||||
|
||||
**Bug fixes**:
|
||||
|
||||
- Fix "key does not exist error" after NLSR starts
|
||||
|
||||
- Update install.sh to call ldconfig after installing ChronoSync
|
||||
|
||||
- Add hyperbolic coordinates to default topology
|
||||
|
||||
**Misc changes**:
|
||||
|
||||
- Add an experiment to test nlsrc
|
||||
|
||||
- Create faces in NFD for each neighbor in NLSR
|
||||
|
||||
- Update to latest ndn-cxx
|
||||
|
||||
- Use /tmp/minindn folder as default work dir instead of /tmp
|
||||
|
||||
Mini-NDN version 0.3.0 (changes since version 0.2.0)
|
||||
----------------------------------------------------
|
||||
|
||||
Release date: March 3, 2017
|
||||
|
||||
**New features**:
|
||||
|
||||
- Mini-NDN cluster edition
|
||||
|
||||
- New experiments for making NLSR testing easier
|
||||
|
||||
**Bug fixes**:
|
||||
|
||||
- Set site name correctly
|
||||
|
||||
- Install missing certificates in NLSR security config
|
||||
|
||||
- Fix quitting of NLSR due to key not found error
|
||||
|
||||
**Misc changes**:
|
||||
|
||||
- Removed nlsr.conf file, generate it within the code
|
||||
|
||||
- Use argparse instead of deprecated optparse
|
||||
|
||||
- Update security config section for NLSR
|
||||
|
||||
- Change mininet prompt to mini-ndn
|
||||
|
||||
- Set network name at one place
|
||||
|
||||
- Update install.sh script to install openssl
|
||||
|
||||
- Update install.sh script to install cryptopp from package instead of
|
||||
compiling from source
|
||||
|
||||
- Update install.sh to clean build folder every time to get rid of
|
||||
removed files such as old experiments
|
||||
|
||||
- Fix old code - use net.hosts instead of storing hosts in a variable
|
||||
|
||||
- Use nfdc instead of deprecated nfd-status
|
||||
|
||||
Mini-NDN version 0.2.0 (changes since version 0.1.1)
|
||||
----------------------------------------------------
|
||||
|
||||
Release date: August 18, 2016
|
||||
|
||||
**New features**:
|
||||
|
||||
- Automatic security configuration for NLSR
|
||||
|
||||
- Use /usr/local/etc/ndn/nfd.conf as default config file for NFD
|
||||
|
||||
- Class to monitor /proc/$PID/stat file for PID
|
||||
|
||||
- Mini-NDN exits gracefully on SIGINT and non-convergence
|
||||
|
||||
- Faster Mini-NDN install script - does not do apt-get update everytime
|
||||
|
||||
- NLSR is launched with explicit config file for easier process
|
||||
identification
|
||||
|
||||
- Add and update more documentation
|
||||
|
||||
**Bug fixes**:
|
||||
|
||||
- NFD is killed correctly on exit
|
||||
|
||||
- Best route strategy is set correctly
|
||||
|
||||
Mini-NDN version 0.1.1 (changes since version 0.1.0)
|
||||
----------------------------------------------------
|
||||
|
||||
Release date: November 4, 2015
|
||||
|
||||
**New features**:
|
||||
|
||||
- Use nfd.conf.sample from currently installed NFD
|
||||
|
||||
- Add working directory option to allow execution environment outside
|
||||
of /tmp
|
||||
|
||||
- Add results directory option to store experiment results after
|
||||
completion
|
||||
|
||||
- Add support for switches in GUI and configuration file
|
||||
|
||||
- Add failNode and recoverNode methods to Experiment class
|
||||
|
||||
- Add most connected node (MCN) failure experiment
|
||||
|
||||
- Add option to specify percentage of nodes pinged
|
||||
|
||||
**Code changes**:
|
||||
|
||||
- Refactor program options into container class
|
||||
|
||||
- Remove unused "FIB Entries" option from NDN host options
|
||||
|
||||
**Bug fixes**:
|
||||
|
||||
- Abort start up if experiment name is invalid
|
||||
|
||||
- Restart pings after recovery in failure experiment
|
||||
|
||||
Mini-NDN version 0.1.0 (initial release)
|
||||
----------------------------------------
|
||||
|
||||
Release date: July 15, 2015
|
||||
|
||||
Mini-NDN is a lightweight networking emulation tool that enables
|
||||
testing, experimentation, and research on the NDN platform. Based on
|
||||
Mininet, Mini-NDN uses the NDN libraries, NFD, NLSR, and tools released
|
||||
by the `NDN project <http://named-data.net/codebase/platform/>`__ to
|
||||
emulate an NDN network on a single system.
|
||||
|
||||
**Included features**:
|
||||
|
||||
- Run a complete NDN network on a single system
|
||||
|
||||
- Automatic configuration of NLSR to provide a routable NDN network
|
||||
|
||||
- Supports user created NDN applications
|
||||
|
||||
- Create a topology using the included Mini-NDN Edit GUI application
|
||||
|
||||
- Allows individual configuration of NFD and NLSR parameters for each
|
||||
node
|
||||
|
||||
- Provides an experiment management framework for easy creation of
|
||||
custom networking experiments
|
||||
|
||||
- Uses a simple topology file format to define hosts, links, and
|
||||
configuration values
|
||||
|
||||
- Configure network link parameters including bandwidth, delay, and
|
||||
loss rate
|
||||
|
||||
- Includes a pre-configured topology file to replicate the NDN testbed
|
||||
@@ -1,18 +0,0 @@
|
||||
Video Tutorials
|
||||
===============
|
||||
|
||||
Maybe outdated since version 0.5.0.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div id="video-container" class="col-md-6 ">
|
||||
<p>Mini-NDN Demo at ACM, 2017</p>
|
||||
<iframe width="400" height="230" src="https://www.youtube.com/embed/xYRPHZe18o0" frameborder="0" allowfullscreen=""></iframe>
|
||||
</div>
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div id="video-container" class="col-md-6 "">
|
||||
<p>Mini-NDN Overview </p>
|
||||
<iframe width="400" height="230" src="https://www.youtube.com/embed/Da7t8yBWzv0" frameborder="0" allowfullscreen="">
|
||||
</div>
|
||||
@@ -0,0 +1,179 @@
|
||||
Mininet Examples
|
||||
========================================================
|
||||
|
||||
These examples are intended to help you get started using
|
||||
Mininet's Python API.
|
||||
|
||||
========================================================
|
||||
|
||||
#### baresshd.py:
|
||||
|
||||
This example uses Mininet's medium-level API to create an sshd
|
||||
process running in a namespace. Doesn't use OpenFlow.
|
||||
|
||||
#### bind.py:
|
||||
|
||||
This example shows how you can create private directories for each
|
||||
node in a Mininet topology.
|
||||
|
||||
#### cluster.py:
|
||||
|
||||
This example contains all of the code for experimental cluster
|
||||
edition. Remote classes and MininetCluster can be imported from
|
||||
here to create a topology with nodes on remote machines.
|
||||
|
||||
#### clusterSanity.py:
|
||||
|
||||
This example runs cluster edition locally as a sanity check to test
|
||||
basic functionality.
|
||||
|
||||
#### clustercli.py:
|
||||
|
||||
This example contains a CLI for experimental cluster edition.
|
||||
|
||||
#### clusterdemo.py:
|
||||
|
||||
This example is a basic demo of cluster edition on 3 servers with
|
||||
a tree topology of depth 3 and fanout 3.
|
||||
|
||||
#### consoles.py:
|
||||
|
||||
This example creates a grid of console windows, one for each node,
|
||||
and allows interaction with and monitoring of each console, including
|
||||
graphical monitoring.
|
||||
|
||||
#### controllers.py:
|
||||
|
||||
This example creates a network with multiple controllers, by
|
||||
using a custom `Switch()` subclass.
|
||||
|
||||
#### controllers2.py:
|
||||
|
||||
This example creates a network with multiple controllers by
|
||||
creating an empty network, adding nodes to it, and manually
|
||||
starting the switches.
|
||||
|
||||
#### controlnet.py:
|
||||
|
||||
This examples shows how you can model the control network as well
|
||||
as the data network, by actually creating two Mininet objects.
|
||||
|
||||
#### cpu.py:
|
||||
|
||||
This example tests iperf bandwidth for varying CPU limits.
|
||||
|
||||
#### emptynet.py:
|
||||
|
||||
This example demonstrates creating an empty network (i.e. with no
|
||||
topology object) and adding nodes to it.
|
||||
|
||||
#### hwintf.py:
|
||||
|
||||
This example shows how to add an interface (for example a real
|
||||
hardware interface) to a network after the network is created.
|
||||
|
||||
#### intfoptions.py:
|
||||
|
||||
This example reconfigures a TCIntf during runtime with different
|
||||
traffic control commands to test bandwidth, loss, and delay.
|
||||
|
||||
#### limit.py:
|
||||
|
||||
This example shows how to use link and CPU limits.
|
||||
|
||||
#### linearbandwidth.py:
|
||||
|
||||
This example shows how to create a custom topology programatically
|
||||
by subclassing Topo, and how to run a series of tests on it.
|
||||
|
||||
#### linuxrouter.py:
|
||||
|
||||
This example shows how to create and configure a router in Mininet
|
||||
that uses Linux IP forwarding.
|
||||
|
||||
#### miniedit.py:
|
||||
|
||||
This example demonstrates creating a network via a graphical editor.
|
||||
|
||||
#### mobility.py:
|
||||
|
||||
This example demonstrates detaching an interface from one switch and
|
||||
attaching it another as a basic way to move a host around a network.
|
||||
|
||||
#### multiLink.py:
|
||||
|
||||
This example demonstrates the creation of multiple links between
|
||||
nodes using a custom Topology class.
|
||||
|
||||
#### multiping.py:
|
||||
|
||||
This example demonstrates one method for
|
||||
monitoring output from multiple hosts, using `node.monitor()`.
|
||||
|
||||
#### multipoll.py:
|
||||
|
||||
This example demonstrates monitoring output files from multiple hosts.
|
||||
|
||||
#### multitest.py:
|
||||
|
||||
This example creates a network and runs multiple tests on it.
|
||||
|
||||
#### nat.py:
|
||||
|
||||
This example shows how to connect a Mininet network to the Internet
|
||||
using NAT. It also answers the eternal question "why can't I ping
|
||||
`google.com`?"
|
||||
|
||||
#### natnet.py:
|
||||
|
||||
This example demonstrates how to create a network using a NAT node
|
||||
to connect hosts to the internet.
|
||||
|
||||
#### numberedports.py:
|
||||
|
||||
This example verifies the mininet ofport numbers match up to the ovs port numbers.
|
||||
It also verifies that the port numbers match up to the interface numbers
|
||||
|
||||
#### popen.py:
|
||||
|
||||
This example monitors a number of hosts using `host.popen()` and
|
||||
`pmonitor()`.
|
||||
|
||||
#### popenpoll.py:
|
||||
|
||||
This example demonstrates monitoring output from multiple hosts using
|
||||
the `node.popen()` interface (which returns `Popen` objects) and `pmonitor()`.
|
||||
|
||||
#### scratchnet.py, scratchnetuser.py:
|
||||
|
||||
These two examples demonstrate how to create a network by using the lowest-
|
||||
level Mininet functions. Generally the higher-level API is easier to use,
|
||||
but scratchnet shows what is going on behind the scenes.
|
||||
|
||||
#### simpleperf.py:
|
||||
|
||||
A simple example of configuring network and CPU bandwidth limits.
|
||||
|
||||
#### sshd.py:
|
||||
|
||||
This example shows how to run an `sshd` process in each host, allowing
|
||||
you to log in via `ssh`. This requires connecting the Mininet data network
|
||||
to an interface in the root namespace (generaly the control network
|
||||
already lives in the root namespace, so it does not need to be explicitly
|
||||
connected.)
|
||||
|
||||
#### tree1024.py:
|
||||
|
||||
This example attempts to create a 1024-host network, and then runs the
|
||||
CLI on it. It may run into scalability limits, depending on available
|
||||
memory and `sysctl` configuration (see `INSTALL`.)
|
||||
|
||||
#### treeping64.py:
|
||||
|
||||
This example creates a 64-host tree network, and attempts to check full
|
||||
connectivity using `ping`, for different switch/datapath types.
|
||||
|
||||
#### vlanhost.py:
|
||||
|
||||
An example of how to subclass Host to use a VLAN on its primary interface.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
"""
|
||||
Mininet Examples
|
||||
See README for details
|
||||
"""
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
||||
|
||||
import sys
|
||||
from mininet.node import Host
|
||||
from mininet.util import ensureRoot, waitListening
|
||||
|
||||
ensureRoot()
|
||||
timeout = 5
|
||||
|
||||
print "*** Creating nodes"
|
||||
h1 = Host( 'h1' )
|
||||
|
||||
root = Host( 'root', inNamespace=False )
|
||||
|
||||
print "*** Creating links"
|
||||
h1.linkTo( root )
|
||||
|
||||
print h1
|
||||
|
||||
print "*** Configuring nodes"
|
||||
h1.setIP( '10.0.0.1', 8 )
|
||||
root.setIP( '10.0.0.2', 8 )
|
||||
|
||||
print "*** Creating banner file"
|
||||
f = open( '/tmp/%s.banner' % h1.name, 'w' )
|
||||
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
|
||||
f.close()
|
||||
|
||||
print "*** Running sshd"
|
||||
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
|
||||
# add arguments from the command line
|
||||
if len( sys.argv ) > 1:
|
||||
cmd += ' ' + ' '.join( sys.argv[ 1: ] )
|
||||
h1.cmd( cmd )
|
||||
listening = waitListening( server=h1, port=22, timeout=timeout )
|
||||
|
||||
if listening:
|
||||
print "*** You may now ssh into", h1.name, "at", h1.IP()
|
||||
else:
|
||||
print ( "*** Warning: after %s seconds, %s is not listening on port 22"
|
||||
% ( timeout, h1.name ) )
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
bind.py: Bind mount example
|
||||
|
||||
This creates hosts with private directories that the user specifies.
|
||||
These hosts may have persistent directories that will be available
|
||||
across multiple mininet session, or temporary directories that will
|
||||
only last for one mininet session. To specify a persistent
|
||||
directory, add a tuple to a list of private directories:
|
||||
|
||||
[ ( 'directory to be mounted on', 'directory to be mounted' ) ]
|
||||
|
||||
String expansion may be used to create a directory template for
|
||||
each host. To do this, add a %(name)s in place of the host name
|
||||
when creating your list of directories:
|
||||
|
||||
[ ( '/var/run', '/tmp/%(name)s/var/run' ) ]
|
||||
|
||||
If no persistent directory is specified, the directories will default
|
||||
to temporary private directories. To do this, simply create a list of
|
||||
directories to be made private. A tmpfs will then be mounted on them.
|
||||
|
||||
You may use both temporary and persistent directories at the same
|
||||
time. In the following privateDirs string, each host will have a
|
||||
persistent directory in the root filesystem at
|
||||
"/tmp/(hostname)/var/run" mounted on "/var/run". Each host will also
|
||||
have a temporary private directory mounted on "/var/log".
|
||||
|
||||
[ ( '/var/run', '/tmp/%(name)s/var/run' ), '/var/log' ]
|
||||
|
||||
This example has both persistent directories mounted on '/var/log'
|
||||
and '/var/run'. It also has a temporary private directory mounted
|
||||
on '/var/mn'
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Host
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from functools import partial
|
||||
|
||||
|
||||
# Sample usage
|
||||
|
||||
def testHostWithPrivateDirs():
|
||||
"Test bind mounts"
|
||||
topo = SingleSwitchTopo( 10 )
|
||||
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
|
||||
( '/var/run', '/tmp/%(name)s/var/run' ),
|
||||
'/var/mn' ]
|
||||
host = partial( Host,
|
||||
privateDirs=privateDirs )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
net.start()
|
||||
directories = [ directory[ 0 ] if isinstance( directory, tuple )
|
||||
else directory for directory in privateDirs ]
|
||||
info( 'Private Directories:', directories, '\n' )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testHostWithPrivateDirs()
|
||||
info( 'Done.\n')
|
||||
@@ -1,95 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
This example demonstrates the functionality of the ndncatchunks and ndnputchunks. There are
|
||||
programs to transfer a file as Data segments in NDN.
|
||||
https://github.com/named-data/ndn-tools/tree/master/tools/chunks.
|
||||
"""
|
||||
|
||||
from time import sleep
|
||||
import subprocess
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
|
||||
def sendFile(node, prefix, file):
|
||||
"""
|
||||
Publish a file using ndnputchunks
|
||||
:parma mininet.node.Host node: mininet node object
|
||||
:param string prefix: prefix to publish the chunks of the file
|
||||
:param string file: file to publish
|
||||
"""
|
||||
info ("File published:", file)
|
||||
cmd = 'ndnputchunks {}/{} < {} > putchunks.log 2>&1 &'.format(prefix, "fname", file)
|
||||
node.cmd(cmd)
|
||||
# Sleep for appropriate time based on the file size
|
||||
sleep(5)
|
||||
|
||||
def receiveFile(node, prefix, filename):
|
||||
"""
|
||||
Fetch a file using ndncatchunks
|
||||
:parma mininet.node.Host node: mininet node object
|
||||
:param string prefix: producer's prefix under which the file the published
|
||||
:param string file: name given to the file that will be received
|
||||
"""
|
||||
info ("Fething file: ", filename)
|
||||
cmd = 'ndncatchunks {}/{} > {} 2> catchunks.log &'.format(prefix, "fname", filename)
|
||||
node.cmd(cmd)
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
# Create a test file to publish
|
||||
testFile = "/tmp/test-chunks"
|
||||
cmd = 'echo "demonstrate file transfer using catchunks and putchunks" > {}'.format(testFile)
|
||||
subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
ndn = Minindn()
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
sleep(70)
|
||||
|
||||
# Default topology is used in this experiment "/topologies/default-topology.conf"
|
||||
# lets make node "a" as a producer node, and node "c" as a conumer node
|
||||
producer = ndn.net['a']
|
||||
producerPrefix = "/test-producer" # prefix under which the file will be published
|
||||
consumer = ndn.net['c']
|
||||
|
||||
# Advertise the producer prefix to the network
|
||||
producer.cmd('nlsrc advertise {}'.format(producerPrefix))
|
||||
sleep (5) # sleep for routing convergence.
|
||||
|
||||
sendFile(producer, producerPrefix, testFile)
|
||||
receiveFile(consumer, producerPrefix, "test-chunks")
|
||||
|
||||
MiniNDNCLI(ndn.net)
|
||||
ndn.stop()
|
||||
Executable
+914
@@ -0,0 +1,914 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
cluster.py: prototyping/experimentation for distributed Mininet,
|
||||
aka Mininet: Cluster Edition
|
||||
|
||||
Author: Bob Lantz
|
||||
|
||||
Core classes:
|
||||
|
||||
RemoteNode: a Node() running on a remote server
|
||||
RemoteOVSSwitch(): an OVSSwitch() running on a remote server
|
||||
RemoteLink: a Link() on a remote server
|
||||
Tunnel: a Link() between a local Node() and a RemoteNode()
|
||||
|
||||
These are largely interoperable with local objects.
|
||||
|
||||
- One Mininet to rule them all
|
||||
|
||||
It is important that the same topologies, APIs, and CLI can be used
|
||||
with minimal or no modification in both local and distributed environments.
|
||||
|
||||
- Multiple placement models
|
||||
|
||||
Placement should be as easy as possible. We should provide basic placement
|
||||
support and also allow for explicit placement.
|
||||
|
||||
Questions:
|
||||
|
||||
What is the basic communication mechanism?
|
||||
|
||||
To start with? Probably a single multiplexed ssh connection between each
|
||||
pair of mininet servers that needs to communicate.
|
||||
|
||||
How are tunnels created?
|
||||
|
||||
We have several options including ssh, GRE, OF capsulator, socat, VDE, l2tp,
|
||||
etc.. It's not clear what the best one is. For now, we use ssh tunnels since
|
||||
they are encrypted and semi-automatically shared. We will probably want to
|
||||
support GRE as well because it's very easy to set up with OVS.
|
||||
|
||||
How are tunnels destroyed?
|
||||
|
||||
They are destroyed when the links are deleted in Mininet.stop()
|
||||
|
||||
How does RemoteNode.popen() work?
|
||||
|
||||
It opens a shared ssh connection to the remote server and attaches to
|
||||
the namespace using mnexec -a -g.
|
||||
|
||||
Is there any value to using Paramiko vs. raw ssh?
|
||||
|
||||
Maybe, but it doesn't seem to support L2 tunneling.
|
||||
|
||||
Should we preflight the entire network, including all server-to-server
|
||||
connections?
|
||||
|
||||
Yes! We don't yet do this with remote server-to-server connections yet.
|
||||
|
||||
Should we multiplex the link ssh connections?
|
||||
|
||||
Yes, this is done automatically with ControlMaster=auto.
|
||||
|
||||
Note on ssh and DNS:
|
||||
Please add UseDNS: no to your /etc/ssh/sshd_config!!!
|
||||
|
||||
Things to do:
|
||||
|
||||
- asynchronous/pipelined/parallel startup
|
||||
- ssh debugging/profiling
|
||||
- make connections into real objects
|
||||
- support for other tunneling schemes
|
||||
- tests and benchmarks
|
||||
- hifi support (e.g. delay compensation)
|
||||
"""
|
||||
|
||||
from mininet.node import Node, Host, OVSSwitch, Controller
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import LinearTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import quietRun, errRun
|
||||
from mininet.examples.clustercli import CLI
|
||||
from mininet.log import setLogLevel, debug, info, error
|
||||
from mininet.clean import addCleanupCallback
|
||||
|
||||
from signal import signal, SIGINT, SIG_IGN
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
import os
|
||||
from random import randrange
|
||||
import sys
|
||||
import re
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
|
||||
def findUser():
|
||||
"Try to return logged-in (usually non-root) user"
|
||||
return (
|
||||
# If we're running sudo
|
||||
os.environ.get( 'SUDO_USER', False ) or
|
||||
# Logged-in user (if we have a tty)
|
||||
( quietRun( 'who am i' ).split() or [ False ] )[ 0 ] or
|
||||
# Give up and return effective user
|
||||
quietRun( 'whoami' ) )
|
||||
|
||||
|
||||
class ClusterCleanup( object ):
|
||||
"Cleanup callback"
|
||||
|
||||
inited = False
|
||||
serveruser = {}
|
||||
|
||||
@classmethod
|
||||
def add( cls, server, user='' ):
|
||||
"Add an entry to server: user dict"
|
||||
if not cls.inited:
|
||||
addCleanupCallback( cls.cleanup )
|
||||
if not user:
|
||||
user = findUser()
|
||||
cls.serveruser[ server ] = user
|
||||
|
||||
@classmethod
|
||||
def cleanup( cls ):
|
||||
"Clean up"
|
||||
info( '*** Cleaning up cluster\n' )
|
||||
for server, user in cls.serveruser.iteritems():
|
||||
if server == 'localhost':
|
||||
# Handled by mininet.clean.cleanup()
|
||||
continue
|
||||
else:
|
||||
cmd = [ 'su', user, '-c',
|
||||
'ssh %s@%s sudo mn -c' % ( user, server ) ]
|
||||
info( cmd, '\n' )
|
||||
info( quietRun( cmd ) )
|
||||
|
||||
# BL note: so little code is required for remote nodes,
|
||||
# we will probably just want to update the main Node()
|
||||
# class to enable it for remote access! However, there
|
||||
# are a large number of potential failure conditions with
|
||||
# remote nodes which we may want to detect and handle.
|
||||
# Another interesting point is that we could put everything
|
||||
# in a mix-in class and easily add cluster mode to 2.0.
|
||||
|
||||
class RemoteMixin( object ):
|
||||
|
||||
"A mix-in class to turn local nodes into remote nodes"
|
||||
|
||||
# ssh base command
|
||||
# -q: don't print stupid diagnostic messages
|
||||
# BatchMode yes: don't ask for password
|
||||
# ForwardAgent yes: forward authentication credentials
|
||||
sshbase = [ 'ssh', '-q',
|
||||
'-o', 'BatchMode=yes',
|
||||
'-o', 'ForwardAgent=yes', '-tt' ]
|
||||
|
||||
def __init__( self, name, server='localhost', user=None, serverIP=None,
|
||||
controlPath=False, splitInit=False, **kwargs):
|
||||
"""Instantiate a remote node
|
||||
name: name of remote node
|
||||
server: remote server (optional)
|
||||
user: user on remote server (optional)
|
||||
controlPath: specify shared ssh control path (optional)
|
||||
splitInit: split initialization?
|
||||
**kwargs: see Node()"""
|
||||
# We connect to servers by IP address
|
||||
self.server = server if server else 'localhost'
|
||||
self.serverIP = ( serverIP if serverIP
|
||||
else self.findServerIP( self.server ) )
|
||||
self.user = user if user else findUser()
|
||||
ClusterCleanup.add( server=server, user=user )
|
||||
if controlPath is True:
|
||||
# Set a default control path for shared SSH connections
|
||||
controlPath = '/tmp/mn-%r@%h:%p'
|
||||
self.controlPath = controlPath
|
||||
self.splitInit = splitInit
|
||||
if self.user and self.server != 'localhost':
|
||||
self.dest = '%s@%s' % ( self.user, self.serverIP )
|
||||
self.sshcmd = [ 'sudo', '-E', '-u', self.user ] + self.sshbase
|
||||
if self.controlPath:
|
||||
self.sshcmd += [ '-o', 'ControlPath=' + self.controlPath,
|
||||
'-o', 'ControlMaster=auto',
|
||||
'-o', 'ControlPersist=' + '1' ]
|
||||
self.sshcmd += [ self.dest ]
|
||||
self.isRemote = True
|
||||
else:
|
||||
self.dest = None
|
||||
self.sshcmd = []
|
||||
self.isRemote = False
|
||||
# Satisfy pylint
|
||||
self.shell, self.pid = None, None
|
||||
super( RemoteMixin, self ).__init__( name, **kwargs )
|
||||
|
||||
# Determine IP address of local host
|
||||
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
|
||||
|
||||
@classmethod
|
||||
def findServerIP( cls, server ):
|
||||
"Return our server's IP address"
|
||||
# First, check for an IP address
|
||||
ipmatch = cls._ipMatchRegex.findall( server )
|
||||
if ipmatch:
|
||||
return ipmatch[ 0 ]
|
||||
# Otherwise, look up remote server
|
||||
output = quietRun( 'getent ahostsv4 %s' % server )
|
||||
ips = cls._ipMatchRegex.findall( output )
|
||||
ip = ips[ 0 ] if ips else None
|
||||
return ip
|
||||
|
||||
# Command support via shell process in namespace
|
||||
def startShell( self, *args, **kwargs ):
|
||||
"Start a shell process for running commands"
|
||||
if self.isRemote:
|
||||
kwargs.update( mnopts='-c' )
|
||||
super( RemoteMixin, self ).startShell( *args, **kwargs )
|
||||
# Optional split initialization
|
||||
self.sendCmd( 'echo $$' )
|
||||
if not self.splitInit:
|
||||
self.finishInit()
|
||||
|
||||
def finishInit( self ):
|
||||
"Wait for split initialization to complete"
|
||||
self.pid = int( self.waitOutput() )
|
||||
|
||||
def rpopen( self, *cmd, **opts ):
|
||||
"Return a Popen object on underlying server in root namespace"
|
||||
params = { 'stdin': PIPE,
|
||||
'stdout': PIPE,
|
||||
'stderr': STDOUT,
|
||||
'sudo': True }
|
||||
params.update( opts )
|
||||
return self._popen( *cmd, **params )
|
||||
|
||||
def rcmd( self, *cmd, **opts):
|
||||
"""rcmd: run a command on underlying server
|
||||
in root namespace
|
||||
args: string or list of strings
|
||||
returns: stdout and stderr"""
|
||||
popen = self.rpopen( *cmd, **opts )
|
||||
# print 'RCMD: POPEN:', popen
|
||||
# These loops are tricky to get right.
|
||||
# Once the process exits, we can read
|
||||
# EOF twice if necessary.
|
||||
result = ''
|
||||
while True:
|
||||
poll = popen.poll()
|
||||
result += popen.stdout.read()
|
||||
if poll is not None:
|
||||
break
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _ignoreSignal():
|
||||
"Detach from process group to ignore all signals"
|
||||
os.setpgrp()
|
||||
|
||||
def _popen( self, cmd, sudo=True, tt=True, **params):
|
||||
"""Spawn a process on a remote node
|
||||
cmd: remote command to run (list)
|
||||
**params: parameters to Popen()
|
||||
returns: Popen() object"""
|
||||
if type( cmd ) is str:
|
||||
cmd = cmd.split()
|
||||
if self.isRemote:
|
||||
if sudo:
|
||||
cmd = [ 'sudo', '-E' ] + cmd
|
||||
if tt:
|
||||
cmd = self.sshcmd + cmd
|
||||
else:
|
||||
# Hack: remove -tt
|
||||
sshcmd = list( self.sshcmd )
|
||||
sshcmd.remove( '-tt' )
|
||||
cmd = sshcmd + cmd
|
||||
else:
|
||||
if self.user and not sudo:
|
||||
# Drop privileges
|
||||
cmd = [ 'sudo', '-E', '-u', self.user ] + cmd
|
||||
params.update( preexec_fn=self._ignoreSignal )
|
||||
debug( '_popen', cmd, '\n' )
|
||||
popen = super( RemoteMixin, self )._popen( cmd, **params )
|
||||
return popen
|
||||
|
||||
def popen( self, *args, **kwargs ):
|
||||
"Override: disable -tt"
|
||||
return super( RemoteMixin, self).popen( *args, tt=False, **kwargs )
|
||||
|
||||
def addIntf( self, *args, **kwargs ):
|
||||
"Override: use RemoteLink.moveIntf"
|
||||
kwargs.update( moveIntfFn=RemoteLink.moveIntf )
|
||||
return super( RemoteMixin, self).addIntf( *args, **kwargs )
|
||||
|
||||
|
||||
class RemoteNode( RemoteMixin, Node ):
|
||||
"A node on a remote server"
|
||||
pass
|
||||
|
||||
|
||||
class RemoteHost( RemoteNode ):
|
||||
"A RemoteHost is simply a RemoteNode"
|
||||
pass
|
||||
|
||||
|
||||
class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
|
||||
"Remote instance of Open vSwitch"
|
||||
|
||||
OVSVersions = {}
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
# No batch startup yet
|
||||
kwargs.update( batch=True )
|
||||
super( RemoteOVSSwitch, self ).__init__( *args, **kwargs )
|
||||
|
||||
def isOldOVS( self ):
|
||||
"Is remote switch using an old OVS version?"
|
||||
cls = type( self )
|
||||
if self.server not in cls.OVSVersions:
|
||||
# pylint: disable=not-callable
|
||||
vers = self.cmd( 'ovs-vsctl --version' )
|
||||
# pylint: enable=not-callable
|
||||
cls.OVSVersions[ self.server ] = re.findall(
|
||||
r'\d+\.\d+', vers )[ 0 ]
|
||||
return ( StrictVersion( cls.OVSVersions[ self.server ] ) <
|
||||
StrictVersion( '1.10' ) )
|
||||
|
||||
@classmethod
|
||||
def batchStartup( cls, switches, **_kwargs ):
|
||||
"Start up switches in per-server batches"
|
||||
key = attrgetter( 'server' )
|
||||
for server, switchGroup in groupby( sorted( switches, key=key ), key ):
|
||||
info( '(%s)' % server )
|
||||
group = tuple( switchGroup )
|
||||
switch = group[ 0 ]
|
||||
OVSSwitch.batchStartup( group, run=switch.cmd )
|
||||
return switches
|
||||
|
||||
@classmethod
|
||||
def batchShutdown( cls, switches, **_kwargs ):
|
||||
"Stop switches in per-server batches"
|
||||
key = attrgetter( 'server' )
|
||||
for server, switchGroup in groupby( sorted( switches, key=key ), key ):
|
||||
info( '(%s)' % server )
|
||||
group = tuple( switchGroup )
|
||||
switch = group[ 0 ]
|
||||
OVSSwitch.batchShutdown( group, run=switch.rcmd )
|
||||
return switches
|
||||
|
||||
|
||||
class RemoteLink( Link ):
|
||||
"A RemoteLink is a link between nodes which may be on different servers"
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"""Initialize a RemoteLink
|
||||
see Link() for parameters"""
|
||||
# Create links on remote node
|
||||
self.node1 = node1
|
||||
self.node2 = node2
|
||||
self.tunnel = None
|
||||
kwargs.setdefault( 'params1', {} )
|
||||
kwargs.setdefault( 'params2', {} )
|
||||
self.cmd = None # satisfy pylint
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
def stop( self ):
|
||||
"Stop this link"
|
||||
if self.tunnel:
|
||||
self.tunnel.terminate()
|
||||
self.intf1.delete()
|
||||
self.intf2.delete()
|
||||
else:
|
||||
Link.stop( self )
|
||||
self.tunnel = None
|
||||
|
||||
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
|
||||
node1=None, node2=None, deleteIntfs=True ):
|
||||
"""Create pair of interfaces
|
||||
intfname1: name of interface 1
|
||||
intfname2: name of interface 2
|
||||
(override this method [and possibly delete()]
|
||||
to change link type)"""
|
||||
node1 = self.node1 if node1 is None else node1
|
||||
node2 = self.node2 if node2 is None else node2
|
||||
server1 = getattr( node1, 'server', 'localhost' )
|
||||
server2 = getattr( node2, 'server', 'localhost' )
|
||||
if server1 == server2:
|
||||
# Link within same server
|
||||
return Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
|
||||
node1, node2, deleteIntfs=deleteIntfs )
|
||||
# Otherwise, make a tunnel
|
||||
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
|
||||
addr1, addr2 )
|
||||
return self.tunnel
|
||||
|
||||
@staticmethod
|
||||
def moveIntf( intf, node, printError=True ):
|
||||
"""Move remote interface from root ns to node
|
||||
intf: string, interface
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns
|
||||
printError: if true, print error"""
|
||||
intf = str( intf )
|
||||
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
|
||||
node.rcmd( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not ' %s:' % intf in links:
|
||||
if printError:
|
||||
error( '*** Error: RemoteLink.moveIntf: ' + intf +
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
return False
|
||||
return True
|
||||
|
||||
def makeTunnel( self, node1, node2, intfname1, intfname2,
|
||||
addr1=None, addr2=None ):
|
||||
"Make a tunnel across switches on different servers"
|
||||
# We should never try to create a tunnel to ourselves!
|
||||
assert node1.server != 'localhost' or node2.server != 'localhost'
|
||||
# And we can't ssh into this server remotely as 'localhost',
|
||||
# so try again swappping node1 and node2
|
||||
if node2.server == 'localhost':
|
||||
return self.makeTunnel( node2, node1, intfname2, intfname1,
|
||||
addr2, addr1 )
|
||||
# 1. Create tap interfaces
|
||||
for node in node1, node2:
|
||||
# For now we are hard-wiring tap9, which we will rename
|
||||
cmd = 'ip tuntap add dev tap9 mode tap user ' + node.user
|
||||
result = node.rcmd( cmd )
|
||||
if result:
|
||||
raise Exception( 'error creating tap9 on %s: %s' %
|
||||
( node, result ) )
|
||||
# 2. Create ssh tunnel between tap interfaces
|
||||
# -n: close stdin
|
||||
dest = '%s@%s' % ( node2.user, node2.serverIP )
|
||||
cmd = [ 'ssh', '-n', '-o', 'Tunnel=Ethernet', '-w', '9:9',
|
||||
dest, 'echo @' ]
|
||||
self.cmd = cmd
|
||||
tunnel = node1.rpopen( cmd, sudo=False )
|
||||
# When we receive the character '@', it means that our
|
||||
# tunnel should be set up
|
||||
debug( 'Waiting for tunnel to come up...\n' )
|
||||
ch = tunnel.stdout.read( 1 )
|
||||
if ch != '@':
|
||||
raise Exception( 'makeTunnel:\n',
|
||||
'Tunnel setup failed for',
|
||||
'%s:%s' % ( node1, node1.dest ), 'to',
|
||||
'%s:%s\n' % ( node2, node2.dest ),
|
||||
'command was:', cmd, '\n' )
|
||||
# 3. Move interfaces if necessary
|
||||
for node in node1, node2:
|
||||
if not self.moveIntf( 'tap9', node ):
|
||||
raise Exception( 'interface move failed on node %s' % node )
|
||||
# 4. Rename tap interfaces to desired names
|
||||
for node, intf, addr in ( ( node1, intfname1, addr1 ),
|
||||
( node2, intfname2, addr2 ) ):
|
||||
if not addr:
|
||||
result = node.cmd( 'ip link set tap9 name', intf )
|
||||
else:
|
||||
result = node.cmd( 'ip link set tap9 name', intf,
|
||||
'address', addr )
|
||||
if result:
|
||||
raise Exception( 'error renaming %s: %s' % ( intf, result ) )
|
||||
return tunnel
|
||||
|
||||
def status( self ):
|
||||
"Detailed representation of link"
|
||||
if self.tunnel:
|
||||
if self.tunnel.poll() is not None:
|
||||
status = "Tunnel EXITED %s" % self.tunnel.returncode
|
||||
else:
|
||||
status = "Tunnel Running (%s: %s)" % (
|
||||
self.tunnel.pid, self.cmd )
|
||||
else:
|
||||
status = "OK"
|
||||
result = "%s %s" % ( Link.status( self ), status )
|
||||
return result
|
||||
|
||||
|
||||
# Some simple placement algorithms for MininetCluster
|
||||
|
||||
class Placer( object ):
|
||||
"Node placement algorithm for MininetCluster"
|
||||
|
||||
def __init__( self, servers=None, nodes=None, hosts=None,
|
||||
switches=None, controllers=None, links=None ):
|
||||
"""Initialize placement object
|
||||
servers: list of servers
|
||||
nodes: list of all nodes
|
||||
hosts: list of hosts
|
||||
switches: list of switches
|
||||
controllers: list of controllers
|
||||
links: list of links
|
||||
(all arguments are optional)
|
||||
returns: server"""
|
||||
self.servers = servers or []
|
||||
self.nodes = nodes or []
|
||||
self.hosts = hosts or []
|
||||
self.switches = switches or []
|
||||
self.controllers = controllers or []
|
||||
self.links = links or []
|
||||
|
||||
def place( self, node ):
|
||||
"Return server for a given node"
|
||||
assert self, node # satisfy pylint
|
||||
# Default placement: run locally
|
||||
return 'localhost'
|
||||
|
||||
|
||||
class RandomPlacer( Placer ):
|
||||
"Random placement"
|
||||
def place( self, nodename ):
|
||||
"""Random placement function
|
||||
nodename: node name"""
|
||||
assert nodename # please pylint
|
||||
# This may be slow with lots of servers
|
||||
return self.servers[ randrange( 0, len( self.servers ) ) ]
|
||||
|
||||
|
||||
class RoundRobinPlacer( Placer ):
|
||||
"""Round-robin placement
|
||||
Note this will usually result in cross-server links between
|
||||
hosts and switches"""
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
Placer.__init__( self, *args, **kwargs )
|
||||
self.next = 0
|
||||
|
||||
def place( self, nodename ):
|
||||
"""Round-robin placement function
|
||||
nodename: node name"""
|
||||
assert nodename # please pylint
|
||||
# This may be slow with lots of servers
|
||||
server = self.servers[ self.next ]
|
||||
self.next = ( self.next + 1 ) % len( self.servers )
|
||||
return server
|
||||
|
||||
|
||||
class SwitchBinPlacer( Placer ):
|
||||
"""Place switches (and controllers) into evenly-sized bins,
|
||||
and attempt to co-locate hosts and switches"""
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
Placer.__init__( self, *args, **kwargs )
|
||||
# Easy lookup for servers and node sets
|
||||
self.servdict = dict( enumerate( self.servers ) )
|
||||
self.hset = frozenset( self.hosts )
|
||||
self.sset = frozenset( self.switches )
|
||||
self.cset = frozenset( self.controllers )
|
||||
# Server and switch placement indices
|
||||
self.placement = self.calculatePlacement()
|
||||
|
||||
@staticmethod
|
||||
def bin( nodes, servers ):
|
||||
"Distribute nodes evenly over servers"
|
||||
# Calculate base bin size
|
||||
nlen = len( nodes )
|
||||
slen = len( servers )
|
||||
# Basic bin size
|
||||
quotient = int( nlen / slen )
|
||||
binsizes = { server: quotient for server in servers }
|
||||
# Distribute remainder
|
||||
remainder = nlen % slen
|
||||
for server in servers[ 0 : remainder ]:
|
||||
binsizes[ server ] += 1
|
||||
# Create binsize[ server ] tickets for each server
|
||||
tickets = sum( [ binsizes[ server ] * [ server ]
|
||||
for server in servers ], [] )
|
||||
# And assign one ticket to each node
|
||||
return { node: ticket for node, ticket in zip( nodes, tickets ) }
|
||||
|
||||
def calculatePlacement( self ):
|
||||
"Pre-calculate node placement"
|
||||
placement = {}
|
||||
# Create host-switch connectivity map,
|
||||
# associating host with last switch that it's
|
||||
# connected to
|
||||
switchFor = {}
|
||||
for src, dst in self.links:
|
||||
if src in self.hset and dst in self.sset:
|
||||
switchFor[ src ] = dst
|
||||
if dst in self.hset and src in self.sset:
|
||||
switchFor[ dst ] = src
|
||||
# Place switches
|
||||
placement = self.bin( self.switches, self.servers )
|
||||
# Place controllers and merge into placement dict
|
||||
placement.update( self.bin( self.controllers, self.servers ) )
|
||||
# Co-locate hosts with their switches
|
||||
for h in self.hosts:
|
||||
if h in placement:
|
||||
# Host is already placed - leave it there
|
||||
continue
|
||||
if h in switchFor:
|
||||
placement[ h ] = placement[ switchFor[ h ] ]
|
||||
else:
|
||||
raise Exception(
|
||||
"SwitchBinPlacer: cannot place isolated host " + h )
|
||||
return placement
|
||||
|
||||
def place( self, node ):
|
||||
"""Simple placement algorithm:
|
||||
place switches into evenly sized bins,
|
||||
and place hosts near their switches"""
|
||||
return self.placement[ node ]
|
||||
|
||||
|
||||
class HostSwitchBinPlacer( Placer ):
|
||||
"""Place switches *and hosts* into evenly-sized bins
|
||||
Note that this will usually result in cross-server
|
||||
links between hosts and switches"""
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
Placer.__init__( self, *args, **kwargs )
|
||||
# Calculate bin sizes
|
||||
scount = len( self.servers )
|
||||
self.hbin = max( int( len( self.hosts ) / scount ), 1 )
|
||||
self.sbin = max( int( len( self.switches ) / scount ), 1 )
|
||||
self.cbin = max( int( len( self.controllers ) / scount ), 1 )
|
||||
info( 'scount:', scount )
|
||||
info( 'bins:', self.hbin, self.sbin, self.cbin, '\n' )
|
||||
self.servdict = dict( enumerate( self.servers ) )
|
||||
self.hset = frozenset( self.hosts )
|
||||
self.sset = frozenset( self.switches )
|
||||
self.cset = frozenset( self.controllers )
|
||||
self.hind, self.sind, self.cind = 0, 0, 0
|
||||
|
||||
def place( self, nodename ):
|
||||
"""Simple placement algorithm:
|
||||
place nodes into evenly sized bins"""
|
||||
# Place nodes into bins
|
||||
if nodename in self.hset:
|
||||
server = self.servdict[ self.hind / self.hbin ]
|
||||
self.hind += 1
|
||||
elif nodename in self.sset:
|
||||
server = self.servdict[ self.sind / self.sbin ]
|
||||
self.sind += 1
|
||||
elif nodename in self.cset:
|
||||
server = self.servdict[ self.cind / self.cbin ]
|
||||
self.cind += 1
|
||||
else:
|
||||
info( 'warning: unknown node', nodename )
|
||||
server = self.servdict[ 0 ]
|
||||
return server
|
||||
|
||||
|
||||
# The MininetCluster class is not strictly necessary.
|
||||
# However, it has several purposes:
|
||||
# 1. To set up ssh connection sharing/multiplexing
|
||||
# 2. To pre-flight the system so that everything is more likely to work
|
||||
# 3. To allow connection/connectivity monitoring
|
||||
# 4. To support pluggable placement algorithms
|
||||
|
||||
class MininetCluster( Mininet ):
|
||||
|
||||
"Cluster-enhanced version of Mininet class"
|
||||
|
||||
# Default ssh command
|
||||
# BatchMode yes: don't ask for password
|
||||
# ForwardAgent yes: forward authentication credentials
|
||||
sshcmd = [ 'ssh', '-o', 'BatchMode=yes', '-o', 'ForwardAgent=yes' ]
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"""servers: a list of servers to use (note: include
|
||||
localhost or None to use local system as well)
|
||||
user: user name for server ssh
|
||||
placement: Placer() subclass"""
|
||||
params = { 'host': RemoteHost,
|
||||
'switch': RemoteOVSSwitch,
|
||||
'link': RemoteLink,
|
||||
'precheck': True }
|
||||
params.update( kwargs )
|
||||
servers = params.pop( 'servers', [ 'localhost' ] )
|
||||
servers = [ s if s else 'localhost' for s in servers ]
|
||||
self.servers = servers
|
||||
self.serverIP = params.pop( 'serverIP', {} )
|
||||
if not self.serverIP:
|
||||
self.serverIP = { server: RemoteMixin.findServerIP( server )
|
||||
for server in self.servers }
|
||||
self.user = params.pop( 'user', findUser() )
|
||||
if params.pop( 'precheck' ):
|
||||
self.precheck()
|
||||
self.connections = {}
|
||||
self.placement = params.pop( 'placement', SwitchBinPlacer )
|
||||
# Make sure control directory exists
|
||||
self.cdir = os.environ[ 'HOME' ] + '/.ssh/mn'
|
||||
errRun( [ 'mkdir', '-p', self.cdir ] )
|
||||
Mininet.__init__( self, *args, **params )
|
||||
|
||||
def popen( self, cmd ):
|
||||
"Popen() for server connections"
|
||||
assert self # please pylint
|
||||
old = signal( SIGINT, SIG_IGN )
|
||||
conn = Popen( cmd, stdin=PIPE, stdout=PIPE, close_fds=True )
|
||||
signal( SIGINT, old )
|
||||
return conn
|
||||
|
||||
def baddLink( self, *args, **kwargs ):
|
||||
"break addlink for testing"
|
||||
pass
|
||||
|
||||
def precheck( self ):
|
||||
"""Pre-check to make sure connection works and that
|
||||
we can call sudo without a password"""
|
||||
result = 0
|
||||
info( '*** Checking servers\n' )
|
||||
for server in self.servers:
|
||||
ip = self.serverIP[ server ]
|
||||
if not server or server == 'localhost':
|
||||
continue
|
||||
info( server, '' )
|
||||
dest = '%s@%s' % ( self.user, ip )
|
||||
cmd = [ 'sudo', '-E', '-u', self.user ]
|
||||
cmd += self.sshcmd + [ '-n', dest, 'sudo true' ]
|
||||
debug( ' '.join( cmd ), '\n' )
|
||||
_out, _err, code = errRun( cmd )
|
||||
if code != 0:
|
||||
error( '\nstartConnection: server connection check failed '
|
||||
'to %s using command:\n%s\n'
|
||||
% ( server, ' '.join( cmd ) ) )
|
||||
result |= code
|
||||
if result:
|
||||
error( '*** Server precheck failed.\n'
|
||||
'*** Make sure that the above ssh command works'
|
||||
' correctly.\n'
|
||||
'*** You may also need to run mn -c on all nodes, and/or\n'
|
||||
'*** use sudo -E.\n' )
|
||||
sys.exit( 1 )
|
||||
info( '\n' )
|
||||
|
||||
def modifiedaddHost( self, *args, **kwargs ):
|
||||
"Slightly modify addHost"
|
||||
assert self # please pylint
|
||||
kwargs[ 'splitInit' ] = True
|
||||
return Mininet.addHost( *args, **kwargs )
|
||||
|
||||
def placeNodes( self ):
|
||||
"""Place nodes on servers (if they don't have a server), and
|
||||
start shell processes"""
|
||||
if not self.servers or not self.topo:
|
||||
# No shirt, no shoes, no service
|
||||
return
|
||||
nodes = self.topo.nodes()
|
||||
placer = self.placement( servers=self.servers,
|
||||
nodes=self.topo.nodes(),
|
||||
hosts=self.topo.hosts(),
|
||||
switches=self.topo.switches(),
|
||||
links=self.topo.links() )
|
||||
for node in nodes:
|
||||
config = self.topo.nodeInfo( node )
|
||||
# keep local server name consistent accross nodes
|
||||
if 'server' in config.keys() and config[ 'server' ] is None:
|
||||
config[ 'server' ] = 'localhost'
|
||||
server = config.setdefault( 'server', placer.place( node ) )
|
||||
if server:
|
||||
config.setdefault( 'serverIP', self.serverIP[ server ] )
|
||||
info( '%s:%s ' % ( node, server ) )
|
||||
key = ( None, server )
|
||||
_dest, cfile, _conn = self.connections.get(
|
||||
key, ( None, None, None ) )
|
||||
if cfile:
|
||||
config.setdefault( 'controlPath', cfile )
|
||||
|
||||
def addController( self, *args, **kwargs ):
|
||||
"Patch to update IP address to global IP address"
|
||||
controller = Mininet.addController( self, *args, **kwargs )
|
||||
# Update IP address for controller that may not be local
|
||||
if ( isinstance( controller, Controller)
|
||||
and controller.IP() == '127.0.0.1'
|
||||
and ' eth0:' in controller.cmd( 'ip link show' ) ):
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
def buildFromTopo( self, *args, **kwargs ):
|
||||
"Start network"
|
||||
info( '*** Placing nodes\n' )
|
||||
self.placeNodes()
|
||||
info( '\n' )
|
||||
Mininet.buildFromTopo( self, *args, **kwargs )
|
||||
|
||||
|
||||
def testNsTunnels():
|
||||
"Test tunnels between nodes in namespaces"
|
||||
net = Mininet( host=RemoteHost, link=RemoteLink )
|
||||
h1 = net.addHost( 'h1' )
|
||||
h2 = net.addHost( 'h2', server='ubuntu2' )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
# Manual topology creation with net.add*()
|
||||
#
|
||||
# This shows how node options may be used to manage
|
||||
# cluster placement using the net.add*() API
|
||||
|
||||
def testRemoteNet( remote='ubuntu2' ):
|
||||
"Test remote Node classes"
|
||||
print '*** Remote Node Test'
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
|
||||
link=RemoteLink )
|
||||
c0 = net.addController( 'c0' )
|
||||
# Make sure controller knows its non-loopback address
|
||||
Intf( 'eth0', node=c0 ).updateIP()
|
||||
print "*** Creating local h1"
|
||||
h1 = net.addHost( 'h1' )
|
||||
print "*** Creating remote h2"
|
||||
h2 = net.addHost( 'h2', server=remote )
|
||||
print "*** Creating local s1"
|
||||
s1 = net.addSwitch( 's1' )
|
||||
print "*** Creating remote s2"
|
||||
s2 = net.addSwitch( 's2', server=remote )
|
||||
print "*** Adding links"
|
||||
net.addLink( h1, s1 )
|
||||
net.addLink( s1, s2 )
|
||||
net.addLink( h2, s2 )
|
||||
net.start()
|
||||
print 'Mininet is running on', quietRun( 'hostname' ).strip()
|
||||
for node in c0, h1, h2, s1, s2:
|
||||
print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip()
|
||||
net.pingAll()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
# High-level/Topo API example
|
||||
#
|
||||
# This shows how existing Mininet topologies may be used in cluster
|
||||
# mode by creating node placement functions and a controller which
|
||||
# can be accessed remotely. This implements a very compatible version
|
||||
# of cluster edition with a minimum of code!
|
||||
|
||||
remoteHosts = [ 'h2' ]
|
||||
remoteSwitches = [ 's2' ]
|
||||
remoteServer = 'ubuntu2'
|
||||
|
||||
def HostPlacer( name, *args, **params ):
|
||||
"Custom Host() constructor which places hosts on servers"
|
||||
if name in remoteHosts:
|
||||
return RemoteHost( name, *args, server=remoteServer, **params )
|
||||
else:
|
||||
return Host( name, *args, **params )
|
||||
|
||||
def SwitchPlacer( name, *args, **params ):
|
||||
"Custom Switch() constructor which places switches on servers"
|
||||
if name in remoteSwitches:
|
||||
return RemoteOVSSwitch( name, *args, server=remoteServer, **params )
|
||||
else:
|
||||
return RemoteOVSSwitch( name, *args, **params )
|
||||
|
||||
def ClusterController( *args, **kwargs):
|
||||
"Custom Controller() constructor which updates its eth0 IP address"
|
||||
controller = Controller( *args, **kwargs )
|
||||
# Find out its IP address so that cluster switches can connect
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
def testRemoteTopo():
|
||||
"Test remote Node classes using Mininet()/Topo() API"
|
||||
topo = LinearTopo( 2 )
|
||||
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
|
||||
link=RemoteLink, controller=ClusterController )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
# Need to test backwards placement, where each host is on
|
||||
# a server other than its switch!! But seriously we could just
|
||||
# do random switch placement rather than completely random
|
||||
# host placement.
|
||||
|
||||
def testRemoteSwitches():
|
||||
"Test with local hosts and remote switches"
|
||||
servers = [ 'localhost', 'ubuntu2']
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
placement=RoundRobinPlacer )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
|
||||
#
|
||||
# For testing and demo purposes it would be nice to draw the
|
||||
# network graph and color it based on server.
|
||||
|
||||
# The MininetCluster() class integrates pluggable placement
|
||||
# functions, for maximum ease of use. MininetCluster() also
|
||||
# pre-flights and multiplexes server connections.
|
||||
|
||||
def testMininetCluster():
|
||||
"Test MininetCluster()"
|
||||
servers = [ 'localhost', 'ubuntu2' ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
def signalTest():
|
||||
"Make sure hosts are robust to signals"
|
||||
h = RemoteHost( 'h0', server='ubuntu1' )
|
||||
h.shell.send_signal( SIGINT )
|
||||
h.shell.poll()
|
||||
if h.shell.returncode is None:
|
||||
print 'OK: ', h, 'has not exited'
|
||||
else:
|
||||
print 'FAILURE:', h, 'exited with code', h.shell.returncode
|
||||
h.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# testRemoteTopo()
|
||||
# testRemoteNet()
|
||||
# testMininetCluster()
|
||||
# testRemoteSwitches()
|
||||
signalTest()
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
A sanity check for cluster edition
|
||||
'''
|
||||
|
||||
from mininet.examples.cluster import MininetCluster
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.examples.clustercli import ClusterCLI as CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
|
||||
def clusterSanity():
|
||||
"Sanity check for cluster mode"
|
||||
topo = SingleSwitchTopo()
|
||||
net = MininetCluster( topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
clusterSanity()
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"CLI for Mininet Cluster Edition prototype demo"
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import output, error
|
||||
|
||||
# pylint: disable=global-statement
|
||||
nx, graphviz_layout, plt = None, None, None # Will be imported on demand
|
||||
|
||||
|
||||
class ClusterCLI( CLI ):
|
||||
"CLI with additional commands for Cluster Edition demo"
|
||||
|
||||
@staticmethod
|
||||
def colorsFor( seq ):
|
||||
"Return a list of background colors for a sequence"
|
||||
colors = [ 'red', 'lightgreen', 'cyan', 'yellow', 'orange',
|
||||
'magenta', 'pink', 'grey', 'brown',
|
||||
'white' ]
|
||||
slen, clen = len( seq ), len( colors )
|
||||
reps = max( 1, slen / clen )
|
||||
colors = colors * reps
|
||||
colors = colors[ 0 : slen ]
|
||||
return colors
|
||||
|
||||
def do_plot( self, _line ):
|
||||
"Plot topology colored by node placement"
|
||||
# Import networkx if needed
|
||||
global nx, plt
|
||||
if not nx:
|
||||
try:
|
||||
# pylint: disable=import-error
|
||||
import networkx
|
||||
nx = networkx # satisfy pylint
|
||||
from matplotlib import pyplot
|
||||
plt = pyplot # satisfiy pylint
|
||||
import pygraphviz
|
||||
assert pygraphviz # silence pyflakes
|
||||
# pylint: enable=import-error
|
||||
except ImportError:
|
||||
error( 'plot requires networkx, matplotlib and pygraphviz - '
|
||||
'please install them and try again\n' )
|
||||
return
|
||||
# Make a networkx Graph
|
||||
g = nx.Graph()
|
||||
mn = self.mn
|
||||
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
|
||||
nodes = hosts + switches
|
||||
g.add_nodes_from( nodes )
|
||||
links = [ ( link.intf1.node, link.intf2.node )
|
||||
for link in self.mn.links ]
|
||||
g.add_edges_from( links )
|
||||
# Pick some shapes and colors
|
||||
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
|
||||
color = dict( zip( servers, self.colorsFor( servers ) ) )
|
||||
# Plot it!
|
||||
pos = nx.graphviz_layout( g )
|
||||
opts = { 'ax': None, 'font_weight': 'bold',
|
||||
'width': 2, 'edge_color': 'darkblue' }
|
||||
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
|
||||
for h in hosts ]
|
||||
scolors = [ color[ getattr( s, 'server', 'localhost' ) ]
|
||||
for s in switches ]
|
||||
nx.draw_networkx( g, pos=pos, nodelist=hosts, node_size=800,
|
||||
label='host', node_color=hcolors, node_shape='s',
|
||||
**opts )
|
||||
nx.draw_networkx( g, pos=pos, nodelist=switches, node_size=1000,
|
||||
node_color=scolors, node_shape='o', **opts )
|
||||
# Get rid of axes, add title, and show
|
||||
fig = plt.gcf()
|
||||
ax = plt.gca()
|
||||
ax.get_xaxis().set_visible( False )
|
||||
ax.get_yaxis().set_visible( False )
|
||||
fig.canvas.set_window_title( 'Mininet')
|
||||
plt.title( 'Node Placement', fontweight='bold' )
|
||||
plt.show()
|
||||
|
||||
def do_status( self, _line ):
|
||||
"Report on node shell status"
|
||||
nodes = self.mn.hosts + self.mn.switches
|
||||
for node in nodes:
|
||||
node.shell.poll()
|
||||
exited = [ node for node in nodes
|
||||
if node.shell.returncode is not None ]
|
||||
if exited:
|
||||
for node in exited:
|
||||
output( '%s has exited with code %d\n'
|
||||
% ( node, node.shell.returncode ) )
|
||||
else:
|
||||
output( 'All nodes are still running.\n' )
|
||||
|
||||
def do_placement( self, _line ):
|
||||
"Describe node placement"
|
||||
mn = self.mn
|
||||
nodes = mn.hosts + mn.switches + mn.controllers
|
||||
for server in mn.servers:
|
||||
names = [ n.name for n in nodes if hasattr( n, 'server' )
|
||||
and n.server == server ]
|
||||
output( '%s: %s\n' % ( server, ' '.join( names ) ) )
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
|
||||
|
||||
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.examples.clustercli import ClusterCLI as CLI
|
||||
|
||||
def demo():
|
||||
"Simple Demo of Cluster Mode"
|
||||
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
demo()
|
||||
Executable
+466
@@ -0,0 +1,466 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
consoles.py: bring up a bunch of miniature consoles on a virtual network
|
||||
|
||||
This demo shows how to monitor a set of nodes by using
|
||||
Node's monitor() and Tkinter's createfilehandler().
|
||||
|
||||
We monitor nodes in a couple of ways:
|
||||
|
||||
- First, each individual node is monitored, and its output is added
|
||||
to its console window
|
||||
|
||||
- Second, each time a console window gets iperf output, it is parsed
|
||||
and accumulated. Once we have output for all consoles, a bar is
|
||||
added to the bandwidth graph.
|
||||
|
||||
The consoles also support limited interaction:
|
||||
|
||||
- Pressing "return" in a console will send a command to it
|
||||
|
||||
- Pressing the console's title button will open up an xterm
|
||||
|
||||
Bob Lantz, April 2010
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from Tkinter import Frame, Button, Label, Text, Scrollbar, Canvas, Wm, READABLE
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.topolib import TreeNet
|
||||
from mininet.term import makeTerms, cleanUpScreens
|
||||
from mininet.util import quietRun
|
||||
|
||||
class Console( Frame ):
|
||||
"A simple console on a host."
|
||||
|
||||
def __init__( self, parent, net, node, height=10, width=32, title='Node' ):
|
||||
Frame.__init__( self, parent )
|
||||
|
||||
self.net = net
|
||||
self.node = node
|
||||
self.prompt = node.name + '# '
|
||||
self.height, self.width, self.title = height, width, title
|
||||
|
||||
# Initialize widget styles
|
||||
self.buttonStyle = { 'font': 'Monaco 7' }
|
||||
self.textStyle = {
|
||||
'font': 'Monaco 7',
|
||||
'bg': 'black',
|
||||
'fg': 'green',
|
||||
'width': self.width,
|
||||
'height': self.height,
|
||||
'relief': 'sunken',
|
||||
'insertbackground': 'green',
|
||||
'highlightcolor': 'green',
|
||||
'selectforeground': 'black',
|
||||
'selectbackground': 'green'
|
||||
}
|
||||
|
||||
# Set up widgets
|
||||
self.text = self.makeWidgets( )
|
||||
self.bindEvents()
|
||||
self.sendCmd( 'export TERM=dumb' )
|
||||
|
||||
self.outputHook = None
|
||||
|
||||
def makeWidgets( self ):
|
||||
"Make a label, a text area, and a scroll bar."
|
||||
|
||||
def newTerm( net=self.net, node=self.node, title=self.title ):
|
||||
"Pop up a new terminal window for a node."
|
||||
net.terms += makeTerms( [ node ], title )
|
||||
label = Button( self, text=self.node.name, command=newTerm,
|
||||
**self.buttonStyle )
|
||||
label.pack( side='top', fill='x' )
|
||||
text = Text( self, wrap='word', **self.textStyle )
|
||||
ybar = Scrollbar( self, orient='vertical', width=7,
|
||||
command=text.yview )
|
||||
text.configure( yscrollcommand=ybar.set )
|
||||
text.pack( side='left', expand=True, fill='both' )
|
||||
ybar.pack( side='right', fill='y' )
|
||||
return text
|
||||
|
||||
def bindEvents( self ):
|
||||
"Bind keyboard and file events."
|
||||
# The text widget handles regular key presses, but we
|
||||
# use special handlers for the following:
|
||||
self.text.bind( '<Return>', self.handleReturn )
|
||||
self.text.bind( '<Control-c>', self.handleInt )
|
||||
self.text.bind( '<KeyPress>', self.handleKey )
|
||||
# This is not well-documented, but it is the correct
|
||||
# way to trigger a file event handler from Tk's
|
||||
# event loop!
|
||||
self.tk.createfilehandler( self.node.stdout, READABLE,
|
||||
self.handleReadable )
|
||||
|
||||
# We're not a terminal (yet?), so we ignore the following
|
||||
# control characters other than [\b\n\r]
|
||||
ignoreChars = re.compile( r'[\x00-\x07\x09\x0b\x0c\x0e-\x1f]+' )
|
||||
|
||||
def append( self, text ):
|
||||
"Append something to our text frame."
|
||||
text = self.ignoreChars.sub( '', text )
|
||||
self.text.insert( 'end', text )
|
||||
self.text.mark_set( 'insert', 'end' )
|
||||
self.text.see( 'insert' )
|
||||
outputHook = lambda x, y: True # make pylint happier
|
||||
if self.outputHook:
|
||||
outputHook = self.outputHook
|
||||
outputHook( self, text )
|
||||
|
||||
def handleKey( self, event ):
|
||||
"If it's an interactive command, send it to the node."
|
||||
char = event.char
|
||||
if self.node.waiting:
|
||||
self.node.write( char )
|
||||
|
||||
def handleReturn( self, event ):
|
||||
"Handle a carriage return."
|
||||
cmd = self.text.get( 'insert linestart', 'insert lineend' )
|
||||
# Send it immediately, if "interactive" command
|
||||
if self.node.waiting:
|
||||
self.node.write( event.char )
|
||||
return
|
||||
# Otherwise send the whole line to the shell
|
||||
pos = cmd.find( self.prompt )
|
||||
if pos >= 0:
|
||||
cmd = cmd[ pos + len( self.prompt ): ]
|
||||
self.sendCmd( cmd )
|
||||
|
||||
# Callback ignores event
|
||||
def handleInt( self, _event=None ):
|
||||
"Handle control-c."
|
||||
self.node.sendInt()
|
||||
|
||||
def sendCmd( self, cmd ):
|
||||
"Send a command to our node."
|
||||
if not self.node.waiting:
|
||||
self.node.sendCmd( cmd )
|
||||
|
||||
def handleReadable( self, _fds, timeoutms=None ):
|
||||
"Handle file readable event."
|
||||
data = self.node.monitor( timeoutms )
|
||||
self.append( data )
|
||||
if not self.node.waiting:
|
||||
# Print prompt
|
||||
self.append( self.prompt )
|
||||
|
||||
def waiting( self ):
|
||||
"Are we waiting for output?"
|
||||
return self.node.waiting
|
||||
|
||||
def waitOutput( self ):
|
||||
"Wait for any remaining output."
|
||||
while self.node.waiting:
|
||||
# A bit of a trade-off here...
|
||||
self.handleReadable( self, timeoutms=1000)
|
||||
self.update()
|
||||
|
||||
def clear( self ):
|
||||
"Clear all of our text."
|
||||
self.text.delete( '1.0', 'end' )
|
||||
|
||||
|
||||
class Graph( Frame ):
|
||||
|
||||
"Graph that we can add bars to over time."
|
||||
|
||||
def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
|
||||
barwidth=10, ymax=3.5,):
|
||||
|
||||
Frame.__init__( self, parent )
|
||||
|
||||
self.bg = bg
|
||||
self.gheight = gheight
|
||||
self.gwidth = gwidth
|
||||
self.barwidth = barwidth
|
||||
self.ymax = float( ymax )
|
||||
self.xpos = 0
|
||||
|
||||
# Create everything
|
||||
self.title, self.scale, self.graph = self.createWidgets()
|
||||
self.updateScrollRegions()
|
||||
self.yview( 'moveto', '1.0' )
|
||||
|
||||
def createScale( self ):
|
||||
"Create a and return a new canvas with scale markers."
|
||||
height = float( self.gheight )
|
||||
width = 25
|
||||
ymax = self.ymax
|
||||
scale = Canvas( self, width=width, height=height,
|
||||
background=self.bg )
|
||||
opts = { 'fill': 'red' }
|
||||
# Draw scale line
|
||||
scale.create_line( width - 1, height, width - 1, 0, **opts )
|
||||
# Draw ticks and numbers
|
||||
for y in range( 0, int( ymax + 1 ) ):
|
||||
ypos = height * (1 - float( y ) / ymax )
|
||||
scale.create_line( width, ypos, width - 10, ypos, **opts )
|
||||
scale.create_text( 10, ypos, text=str( y ), **opts )
|
||||
return scale
|
||||
|
||||
def updateScrollRegions( self ):
|
||||
"Update graph and scale scroll regions."
|
||||
ofs = 20
|
||||
height = self.gheight + ofs
|
||||
self.graph.configure( scrollregion=( 0, -ofs,
|
||||
self.xpos * self.barwidth, height ) )
|
||||
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
|
||||
|
||||
def yview( self, *args ):
|
||||
"Scroll both scale and graph."
|
||||
self.graph.yview( *args )
|
||||
self.scale.yview( *args )
|
||||
|
||||
def createWidgets( self ):
|
||||
"Create initial widget set."
|
||||
|
||||
# Objects
|
||||
title = Label( self, text='Bandwidth (Gb/s)', bg=self.bg )
|
||||
width = self.gwidth
|
||||
height = self.gheight
|
||||
scale = self.createScale()
|
||||
graph = Canvas( self, width=width, height=height, background=self.bg)
|
||||
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
|
||||
ybar = Scrollbar( self, orient='vertical', command=self.yview )
|
||||
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
|
||||
scrollregion=(0, 0, width, height ) )
|
||||
scale.configure( yscrollcommand=ybar.set )
|
||||
|
||||
# Layout
|
||||
title.grid( row=0, columnspan=3, sticky='new')
|
||||
scale.grid( row=1, column=0, sticky='nsew' )
|
||||
graph.grid( row=1, column=1, sticky='nsew' )
|
||||
ybar.grid( row=1, column=2, sticky='ns' )
|
||||
xbar.grid( row=2, column=0, columnspan=2, sticky='ew' )
|
||||
self.rowconfigure( 1, weight=1 )
|
||||
self.columnconfigure( 1, weight=1 )
|
||||
return title, scale, graph
|
||||
|
||||
def addBar( self, yval ):
|
||||
"Add a new bar to our graph."
|
||||
percent = yval / self.ymax
|
||||
c = self.graph
|
||||
x0 = self.xpos * self.barwidth
|
||||
x1 = x0 + self.barwidth
|
||||
y0 = self.gheight
|
||||
y1 = ( 1 - percent ) * self.gheight
|
||||
c.create_rectangle( x0, y0, x1, y1, fill='green' )
|
||||
self.xpos += 1
|
||||
self.updateScrollRegions()
|
||||
self.graph.xview( 'moveto', '1.0' )
|
||||
|
||||
def clear( self ):
|
||||
"Clear graph contents."
|
||||
self.graph.delete( 'all' )
|
||||
self.xpos = 0
|
||||
|
||||
def test( self ):
|
||||
"Add a bar for testing purposes."
|
||||
ms = 1000
|
||||
if self.xpos < 10:
|
||||
self.addBar( self.xpos / 10 * self.ymax )
|
||||
self.after( ms, self.test )
|
||||
|
||||
def setTitle( self, text ):
|
||||
"Set graph title"
|
||||
self.title.configure( text=text, font='Helvetica 9 bold' )
|
||||
|
||||
|
||||
class ConsoleApp( Frame ):
|
||||
|
||||
"Simple Tk consoles for Mininet."
|
||||
|
||||
menuStyle = { 'font': 'Geneva 7 bold' }
|
||||
|
||||
def __init__( self, net, parent=None, width=4 ):
|
||||
Frame.__init__( self, parent )
|
||||
self.top = self.winfo_toplevel()
|
||||
self.top.title( 'Mininet' )
|
||||
self.net = net
|
||||
self.menubar = self.createMenuBar()
|
||||
cframe = self.cframe = Frame( self )
|
||||
self.consoles = {} # consoles themselves
|
||||
titles = {
|
||||
'hosts': 'Host',
|
||||
'switches': 'Switch',
|
||||
'controllers': 'Controller'
|
||||
}
|
||||
for name in titles:
|
||||
nodes = getattr( net, name )
|
||||
frame, consoles = self.createConsoles(
|
||||
cframe, nodes, width, titles[ name ] )
|
||||
self.consoles[ name ] = Object( frame=frame, consoles=consoles )
|
||||
self.selected = None
|
||||
self.select( 'hosts' )
|
||||
self.cframe.pack( expand=True, fill='both' )
|
||||
cleanUpScreens()
|
||||
# Close window gracefully
|
||||
Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
|
||||
|
||||
# Initialize graph
|
||||
graph = Graph( cframe )
|
||||
self.consoles[ 'graph' ] = Object( frame=graph, consoles=[ graph ] )
|
||||
self.graph = graph
|
||||
self.graphVisible = False
|
||||
self.updates = 0
|
||||
self.hostCount = len( self.consoles[ 'hosts' ].consoles )
|
||||
self.bw = 0
|
||||
|
||||
self.pack( expand=True, fill='both' )
|
||||
|
||||
def updateGraph( self, _console, output ):
|
||||
"Update our graph."
|
||||
m = re.search( r'(\d+.?\d*) ([KMG]?bits)/sec', output )
|
||||
if not m:
|
||||
return
|
||||
val, units = float( m.group( 1 ) ), m.group( 2 )
|
||||
#convert to Gbps
|
||||
if units[0] == 'M':
|
||||
val *= 10 ** -3
|
||||
elif units[0] == 'K':
|
||||
val *= 10 ** -6
|
||||
elif units[0] == 'b':
|
||||
val *= 10 ** -9
|
||||
self.updates += 1
|
||||
self.bw += val
|
||||
if self.updates >= self.hostCount:
|
||||
self.graph.addBar( self.bw )
|
||||
self.bw = 0
|
||||
self.updates = 0
|
||||
|
||||
def setOutputHook( self, fn=None, consoles=None ):
|
||||
"Register fn as output hook [on specific consoles.]"
|
||||
if consoles is None:
|
||||
consoles = self.consoles[ 'hosts' ].consoles
|
||||
for console in consoles:
|
||||
console.outputHook = fn
|
||||
|
||||
def createConsoles( self, parent, nodes, width, title ):
|
||||
"Create a grid of consoles in a frame."
|
||||
f = Frame( parent )
|
||||
# Create consoles
|
||||
consoles = []
|
||||
index = 0
|
||||
for node in nodes:
|
||||
console = Console( f, self.net, node, title=title )
|
||||
consoles.append( console )
|
||||
row = index / width
|
||||
column = index % width
|
||||
console.grid( row=row, column=column, sticky='nsew' )
|
||||
index += 1
|
||||
f.rowconfigure( row, weight=1 )
|
||||
f.columnconfigure( column, weight=1 )
|
||||
return f, consoles
|
||||
|
||||
def select( self, groupName ):
|
||||
"Select a group of consoles to display."
|
||||
if self.selected is not None:
|
||||
self.selected.frame.pack_forget()
|
||||
self.selected = self.consoles[ groupName ]
|
||||
self.selected.frame.pack( expand=True, fill='both' )
|
||||
|
||||
def createMenuBar( self ):
|
||||
"Create and return a menu (really button) bar."
|
||||
f = Frame( self )
|
||||
buttons = [
|
||||
( 'Hosts', lambda: self.select( 'hosts' ) ),
|
||||
( 'Switches', lambda: self.select( 'switches' ) ),
|
||||
( 'Controllers', lambda: self.select( 'controllers' ) ),
|
||||
( 'Graph', lambda: self.select( 'graph' ) ),
|
||||
( 'Ping', self.ping ),
|
||||
( 'Iperf', self.iperf ),
|
||||
( 'Interrupt', self.stop ),
|
||||
( 'Clear', self.clear ),
|
||||
( 'Quit', self.quit )
|
||||
]
|
||||
for name, cmd in buttons:
|
||||
b = Button( f, text=name, command=cmd, **self.menuStyle )
|
||||
b.pack( side='left' )
|
||||
f.pack( padx=4, pady=4, fill='x' )
|
||||
return f
|
||||
|
||||
def clear( self ):
|
||||
"Clear selection."
|
||||
for console in self.selected.consoles:
|
||||
console.clear()
|
||||
|
||||
def waiting( self, consoles=None ):
|
||||
"Are any of our hosts waiting for output?"
|
||||
if consoles is None:
|
||||
consoles = self.consoles[ 'hosts' ].consoles
|
||||
for console in consoles:
|
||||
if console.waiting():
|
||||
return True
|
||||
return False
|
||||
|
||||
def ping( self ):
|
||||
"Tell each host to ping the next one."
|
||||
consoles = self.consoles[ 'hosts' ].consoles
|
||||
if self.waiting( consoles ):
|
||||
return
|
||||
count = len( consoles )
|
||||
i = 0
|
||||
for console in consoles:
|
||||
i = ( i + 1 ) % count
|
||||
ip = consoles[ i ].node.IP()
|
||||
console.sendCmd( 'ping ' + ip )
|
||||
|
||||
def iperf( self ):
|
||||
"Tell each host to iperf to the next one."
|
||||
consoles = self.consoles[ 'hosts' ].consoles
|
||||
if self.waiting( consoles ):
|
||||
return
|
||||
count = len( consoles )
|
||||
self.setOutputHook( self.updateGraph )
|
||||
for console in consoles:
|
||||
# Sometimes iperf -sD doesn't return,
|
||||
# so we run it in the background instead
|
||||
console.node.cmd( 'iperf -s &' )
|
||||
i = 0
|
||||
for console in consoles:
|
||||
i = ( i + 1 ) % count
|
||||
ip = consoles[ i ].node.IP()
|
||||
console.sendCmd( 'iperf -t 99999 -i 1 -c ' + ip )
|
||||
|
||||
def stop( self, wait=True ):
|
||||
"Interrupt all hosts."
|
||||
consoles = self.consoles[ 'hosts' ].consoles
|
||||
for console in consoles:
|
||||
console.handleInt()
|
||||
if wait:
|
||||
for console in consoles:
|
||||
console.waitOutput()
|
||||
self.setOutputHook( None )
|
||||
# Shut down any iperfs that might still be running
|
||||
quietRun( 'killall -9 iperf' )
|
||||
|
||||
def quit( self ):
|
||||
"Stop everything and quit."
|
||||
self.stop( wait=False)
|
||||
Frame.quit( self )
|
||||
|
||||
|
||||
# Make it easier to construct and assign objects
|
||||
|
||||
def assign( obj, **kwargs ):
|
||||
"Set a bunch of fields in an object."
|
||||
obj.__dict__.update( kwargs )
|
||||
|
||||
class Object( object ):
|
||||
"Generic object you can stuff junk into."
|
||||
def __init__( self, **kwargs ):
|
||||
assign( self, **kwargs )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
network = TreeNet( depth=2, fanout=4 )
|
||||
network.start()
|
||||
app = ConsoleApp( network, width=4 )
|
||||
app.mainloop()
|
||||
network.stop()
|
||||
@@ -1,70 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from time import sleep
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
|
||||
|
||||
"""
|
||||
This example demonstrates a basic consumer-producer using Mini-NDN. It uses ndnpeek and ndnpoke as
|
||||
a consumer and a producer respectively. If you want to build your own consumer/producer program, you
|
||||
can take help from here: https://github.com/named-data/ndn-cxx/tree/master/examples and
|
||||
here: https://github.com/dulalsaurab/multicast-supression-ndn/tree/main/ndn-src/consumer-producer
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
ndn = Minindn()
|
||||
ndn.start()
|
||||
|
||||
info('Starting nfd and nlsr on nodes')
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
sleep(90)
|
||||
|
||||
# Default topology is used in this experiment "/topologies/default-topology.conf"
|
||||
# lets make node "a" as a producer node, and node "c" as a consumer node
|
||||
producer = ndn.net['a']
|
||||
consumer = ndn.net['c']
|
||||
|
||||
# start producer
|
||||
producerPrefix = "/example"
|
||||
producer.cmd('nlsrc advertise {}'.format(producerPrefix))
|
||||
sleep(5) # sleep for routing convergence
|
||||
|
||||
# Make sure that basic consumer/producer example are compiled and installed in the system
|
||||
info('Starting consumer and producer application')
|
||||
producer.cmd("echo 'HELLO WORLD' | ndnpoke {} &> producer.log &".format(producerPrefix))
|
||||
consumer.cmd("ndnpeek -p {} &> consumer.log &".format(producerPrefix))
|
||||
|
||||
MiniNDNCLI(ndn.net)
|
||||
ndn.stop()
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Create a network where different switches are connected to
|
||||
different controllers, by creating a custom Switch() subclass.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import OVSSwitch, Controller, RemoteController
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.cli import CLI
|
||||
|
||||
setLogLevel( 'info' )
|
||||
|
||||
# Two local and one "external" controller (which is actually c0)
|
||||
# Ignore the warning message that the remote isn't (yet) running
|
||||
c0 = Controller( 'c0', port=6633 )
|
||||
c1 = Controller( 'c1', port=6634 )
|
||||
c2 = RemoteController( 'c2', ip='127.0.0.1' )
|
||||
|
||||
cmap = { 's1': c0, 's2': c1, 's3': c2 }
|
||||
|
||||
class MultiSwitch( OVSSwitch ):
|
||||
"Custom Switch() subclass that connects to different controllers"
|
||||
def start( self, controllers ):
|
||||
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
|
||||
|
||||
topo = TreeTopo( depth=2, fanout=2 )
|
||||
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
|
||||
for c in [ c0, c1 ]:
|
||||
net.addController(c)
|
||||
net.build()
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example creates a multi-controller network from semi-scratch by
|
||||
using the net.add*() API and manually starting the switches and controllers.
|
||||
|
||||
This is the "mid-level" API, which is an alternative to the "high-level"
|
||||
Topo() API which supports parametrized topology classes.
|
||||
|
||||
Note that one could also create a custom switch class and pass it into
|
||||
the Mininet() constructor.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller, OVSSwitch
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
def multiControllerNet():
|
||||
"Create a network from semi-scratch with multiple controllers."
|
||||
|
||||
net = Mininet( controller=Controller, switch=OVSSwitch )
|
||||
|
||||
print "*** Creating (reference) controllers"
|
||||
c1 = net.addController( 'c1', port=6633 )
|
||||
c2 = net.addController( 'c2', port=6634 )
|
||||
|
||||
print "*** Creating switches"
|
||||
s1 = net.addSwitch( 's1' )
|
||||
s2 = net.addSwitch( 's2' )
|
||||
|
||||
print "*** Creating hosts"
|
||||
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
|
||||
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
|
||||
|
||||
print "*** Creating links"
|
||||
for h in hosts1:
|
||||
net.addLink( s1, h )
|
||||
for h in hosts2:
|
||||
net.addLink( s2, h )
|
||||
net.addLink( s1, s2 )
|
||||
|
||||
print "*** Starting network"
|
||||
net.build()
|
||||
c1.start()
|
||||
c2.start()
|
||||
s1.start( [ c1 ] )
|
||||
s2.start( [ c2 ] )
|
||||
|
||||
print "*** Testing network"
|
||||
net.pingAll()
|
||||
|
||||
print "*** Running CLI"
|
||||
CLI( net )
|
||||
|
||||
print "*** Stopping network"
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' ) # for CLI output
|
||||
multiControllerNet()
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
controlnet.py: Mininet with a custom control network
|
||||
|
||||
We create two Mininet() networks, a control network
|
||||
and a data network, running four DataControllers on the
|
||||
control network to control the data network.
|
||||
|
||||
Since we're using UserSwitch on the data network,
|
||||
it should correctly fail over to a backup controller.
|
||||
|
||||
We also use a Mininet Facade to talk to both the
|
||||
control and data networks from a single CLI.
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller, UserSwitch
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import Topo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
# Some minor hacks
|
||||
|
||||
class DataController( Controller ):
|
||||
"""Data Network Controller.
|
||||
patched to avoid checkListening error and to delete intfs"""
|
||||
|
||||
def checkListening( self ):
|
||||
"Ignore spurious error"
|
||||
pass
|
||||
|
||||
def stop( self, *args, **kwargs ):
|
||||
"Make sure intfs are deleted"
|
||||
kwargs.update( deleteIntfs=True )
|
||||
super( DataController, self ).stop( *args, **kwargs )
|
||||
|
||||
|
||||
class MininetFacade( object ):
|
||||
"""Mininet object facade that allows a single CLI to
|
||||
talk to one or more networks"""
|
||||
|
||||
def __init__( self, net, *args, **kwargs ):
|
||||
"""Create MininetFacade object.
|
||||
net: Primary Mininet object
|
||||
args: unnamed networks passed as arguments
|
||||
kwargs: named networks passed as arguments"""
|
||||
self.net = net
|
||||
self.nets = [ net ] + list( args ) + kwargs.values()
|
||||
self.nameToNet = kwargs
|
||||
self.nameToNet['net'] = net
|
||||
|
||||
def __getattr__( self, name ):
|
||||
"returns attribute from Primary Mininet object"
|
||||
return getattr( self.net, name )
|
||||
|
||||
def __getitem__( self, key ):
|
||||
"returns primary/named networks or node from any net"
|
||||
#search kwargs for net named key
|
||||
if key in self.nameToNet:
|
||||
return self.nameToNet[ key ]
|
||||
#search each net for node named key
|
||||
for net in self.nets:
|
||||
if key in net:
|
||||
return net[ key ]
|
||||
|
||||
def __iter__( self ):
|
||||
"Iterate through all nodes in all Mininet objects"
|
||||
for net in self.nets:
|
||||
for node in net:
|
||||
yield node
|
||||
|
||||
def __len__( self ):
|
||||
"returns aggregate number of nodes in all nets"
|
||||
count = 0
|
||||
for net in self.nets:
|
||||
count += len(net)
|
||||
return count
|
||||
|
||||
def __contains__( self, key ):
|
||||
"returns True if node is a member of any net"
|
||||
return key in self.keys()
|
||||
|
||||
def keys( self ):
|
||||
"returns a list of all node names in all networks"
|
||||
return list( self )
|
||||
|
||||
def values( self ):
|
||||
"returns a list of all nodes in all networks"
|
||||
return [ self[ key ] for key in self ]
|
||||
|
||||
def items( self ):
|
||||
"returns (key,value) tuple list for every node in all networks"
|
||||
return zip( self.keys(), self.values() )
|
||||
|
||||
# A real control network!
|
||||
|
||||
class ControlNetwork( Topo ):
|
||||
"Control Network Topology"
|
||||
def __init__( self, n, dataController=DataController, **kwargs ):
|
||||
"""n: number of data network controller nodes
|
||||
dataController: class for data network controllers"""
|
||||
Topo.__init__( self, **kwargs )
|
||||
# Connect everything to a single switch
|
||||
cs0 = self.addSwitch( 'cs0' )
|
||||
# Add hosts which will serve as data network controllers
|
||||
for i in range( 0, n ):
|
||||
c = self.addHost( 'c%s' % i, cls=dataController,
|
||||
inNamespace=True )
|
||||
self.addLink( c, cs0 )
|
||||
# Connect switch to root namespace so that data network
|
||||
# switches will be able to talk to us
|
||||
root = self.addHost( 'root', inNamespace=False )
|
||||
self.addLink( root, cs0 )
|
||||
|
||||
|
||||
# Make it Happen!!
|
||||
|
||||
def run():
|
||||
"Create control and data networks, and invoke the CLI"
|
||||
|
||||
info( '* Creating Control Network\n' )
|
||||
ctopo = ControlNetwork( n=4, dataController=DataController )
|
||||
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
|
||||
info( '* Adding Control Network Controller\n')
|
||||
cnet.addController( 'cc0', controller=Controller )
|
||||
info( '* Starting Control Network\n')
|
||||
cnet.start()
|
||||
|
||||
info( '* Creating Data Network\n' )
|
||||
topo = TreeTopo( depth=2, fanout=2 )
|
||||
# UserSwitch so we can easily test failover
|
||||
sw = partial( UserSwitch, opts='--inactivity-probe=1 --max-backoff=1' )
|
||||
net = Mininet( topo=topo, switch=sw, controller=None )
|
||||
info( '* Adding Controllers to Data Network\n' )
|
||||
for host in cnet.hosts:
|
||||
if isinstance(host, Controller):
|
||||
net.addController( host )
|
||||
info( '* Starting Data Network\n')
|
||||
net.start()
|
||||
|
||||
mn = MininetFacade( net, cnet=cnet )
|
||||
|
||||
CLI( mn )
|
||||
|
||||
info( '* Stopping Data Network\n' )
|
||||
net.stop()
|
||||
|
||||
info( '* Stopping Control Network\n' )
|
||||
cnet.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
run()
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
cpu.py: test iperf bandwidth for varying cpu limits
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, waitListening
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
|
||||
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
cpu: cpu limit as fraction of overall CPU time"""
|
||||
|
||||
topo = TreeTopo( depth=1, fanout=2 )
|
||||
|
||||
results = {}
|
||||
|
||||
for sched in 'rt', 'cfs':
|
||||
print '*** Testing with', sched, 'bandwidth limiting'
|
||||
for cpu in cpuLimits:
|
||||
host = custom( CPULimitedHost, sched=sched,
|
||||
period_us=period_us,
|
||||
cpu=cpu )
|
||||
try:
|
||||
net = Mininet( topo=topo, host=host )
|
||||
# pylint: disable=bare-except
|
||||
except:
|
||||
info( '*** Skipping host %s\n' % sched )
|
||||
break
|
||||
net.start()
|
||||
net.pingAll()
|
||||
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
|
||||
client, server = hosts[ 0 ], hosts[ -1 ]
|
||||
server.cmd( 'iperf -s -p 5001 &' )
|
||||
waitListening( client, server, 5001 )
|
||||
result = client.cmd( 'iperf -yc -t %s -c %s' % (
|
||||
seconds, server.IP() ) ).split( ',' )
|
||||
bps = float( result[ -1 ] )
|
||||
server.cmdPrint( 'kill %iperf' )
|
||||
net.stop()
|
||||
updated = results.get( sched, [] )
|
||||
updated += [ ( cpu, bps ) ]
|
||||
results[ sched ] = updated
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def dump( results ):
|
||||
"Dump results"
|
||||
|
||||
fmt = '%s\t%s\t%s'
|
||||
|
||||
print
|
||||
print fmt % ( 'sched', 'cpu', 'client MB/s' )
|
||||
print
|
||||
|
||||
for sched in sorted( results.keys() ):
|
||||
entries = results[ sched ]
|
||||
for cpu, bps in entries:
|
||||
pct = '%.2f%%' % ( cpu * 100 )
|
||||
mbps = bps / 1e6
|
||||
print fmt % ( sched, pct, mbps )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
limits = [ .45, .4, .3, .2, .1 ]
|
||||
out = bwtest( limits )
|
||||
dump( out )
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example shows how to create an empty Mininet object
|
||||
(without a topology object) and add nodes to it manually.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
def emptyNet():
|
||||
|
||||
"Create an empty network and add nodes to it."
|
||||
|
||||
net = Mininet( controller=Controller )
|
||||
|
||||
info( '*** Adding controller\n' )
|
||||
net.addController( 'c0' )
|
||||
|
||||
info( '*** Adding hosts\n' )
|
||||
h1 = net.addHost( 'h1', ip='10.0.0.1' )
|
||||
h2 = net.addHost( 'h2', ip='10.0.0.2' )
|
||||
|
||||
info( '*** Adding switch\n' )
|
||||
s3 = net.addSwitch( 's3' )
|
||||
|
||||
info( '*** Creating links\n' )
|
||||
net.addLink( h1, s3 )
|
||||
net.addLink( h2, s3 )
|
||||
|
||||
info( '*** Starting network\n')
|
||||
net.start()
|
||||
|
||||
info( '*** Running CLI\n' )
|
||||
CLI( net )
|
||||
|
||||
info( '*** Stopping network' )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
emptyNet()
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example shows how to add an interface (for example a real
|
||||
hardware interface) to a network after the network is created.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel, info, error
|
||||
from mininet.net import Mininet
|
||||
from mininet.link import Intf
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import quietRun
|
||||
|
||||
def checkIntf( intf ):
|
||||
"Make sure intf exists and is not configured."
|
||||
if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
|
||||
error( 'Error:', intf, 'does not exist!\n' )
|
||||
exit( 1 )
|
||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
|
||||
if ips:
|
||||
error( 'Error:', intf, 'has an IP address,'
|
||||
'and is probably in use!\n' )
|
||||
exit( 1 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
|
||||
# try to get hw intf from the command line; by default, use eth1
|
||||
intfName = sys.argv[ 1 ] if len( sys.argv ) > 1 else 'eth1'
|
||||
info( '*** Connecting to hw intf: %s' % intfName )
|
||||
|
||||
info( '*** Checking', intfName, '\n' )
|
||||
checkIntf( intfName )
|
||||
|
||||
info( '*** Creating network\n' )
|
||||
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
|
||||
|
||||
switch = net.switches[ 0 ]
|
||||
info( '*** Adding hardware interface', intfName, 'to switch',
|
||||
switch.name, '\n' )
|
||||
_intf = Intf( intfName, node=switch )
|
||||
|
||||
info( '*** Note: you may need to reconfigure the interfaces for '
|
||||
'the Mininet hosts:\n', net.hosts, '\n' )
|
||||
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
'''
|
||||
example of using various TCIntf options.
|
||||
reconfigures a single interface using intf.config()
|
||||
to use different traffic control commands to test
|
||||
bandwidth, loss, and delay
|
||||
'''
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.link import TCLink
|
||||
|
||||
def intfOptions():
|
||||
"run various traffic control commands on a single interface"
|
||||
net = Mininet( autoStaticArp=True )
|
||||
net.addController( 'c0' )
|
||||
h1 = net.addHost( 'h1' )
|
||||
h2 = net.addHost( 'h2' )
|
||||
s1 = net.addSwitch( 's1' )
|
||||
link1 = net.addLink( h1, s1, cls=TCLink )
|
||||
net.addLink( h2, s1 )
|
||||
net.start()
|
||||
|
||||
# flush out latency from reactive forwarding delay
|
||||
net.pingAll()
|
||||
|
||||
info( '\n*** Configuring one intf with bandwidth of 5 Mb\n' )
|
||||
link1.intf1.config( bw=5 )
|
||||
info( '\n*** Running iperf to test\n' )
|
||||
net.iperf()
|
||||
|
||||
info( '\n*** Configuring one intf with loss of 50%\n' )
|
||||
link1.intf1.config( loss=50 )
|
||||
info( '\n' )
|
||||
net.iperf( ( h1, h2 ), l4Type='UDP' )
|
||||
|
||||
info( '\n*** Configuring one intf with delay of 15ms\n' )
|
||||
link1.intf1.config( delay='15ms' )
|
||||
info( '\n*** Run a ping to confirm delay\n' )
|
||||
net.pingPairFull()
|
||||
|
||||
info( '\n*** Done testing\n' )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
intfOptions()
|
||||
@@ -1,62 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2020, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.ip_routing_helper import IPRoutingHelper
|
||||
|
||||
"""
|
||||
This scenario demonstrates the functionality of the IPRoutingHelper. First, the routing helper
|
||||
calculates and configures routes between all nodes and then calls the `pingAll` command to
|
||||
demonstrate that all nodes are reachable.
|
||||
Successful experiments end with: `*** Results: 0% dropped`
|
||||
|
||||
To demonstrate the IPRoutingHelper in more complex scenarios, consider starting the experiment with
|
||||
the Geant-Topology (topologies/geant.conf).
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
ndn = Minindn()
|
||||
|
||||
ndn.start()
|
||||
|
||||
info('Starting NFD on nodes\n')
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
info('Starting NLSR on nodes\n')
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
|
||||
# Calculate all routes for IP routing
|
||||
IPRoutingHelper.calcAllRoutes(ndn.net)
|
||||
info("IP routes configured, start ping\n")
|
||||
|
||||
ndn.net.pingAll()
|
||||
|
||||
ndn.stop()
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
limit.py: example of using link and CPU limits
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.link import TCIntf
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, quietRun
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
|
||||
def testLinkLimit( net, bw ):
|
||||
"Run bandwidth limit test"
|
||||
info( '*** Testing network %.2f Mbps bandwidth limit\n' % bw )
|
||||
net.iperf()
|
||||
|
||||
def limit( bw=10, cpu=.1 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
bw: interface bandwidth limit in Mbps
|
||||
cpu: cpu limit as fraction of overall CPU time"""
|
||||
intf = custom( TCIntf, bw=bw )
|
||||
myTopo = TreeTopo( depth=1, fanout=2 )
|
||||
for sched in 'rt', 'cfs':
|
||||
info( '*** Testing with', sched, 'bandwidth limiting\n' )
|
||||
if sched == 'rt':
|
||||
release = quietRun( 'uname -r' ).strip('\r\n')
|
||||
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s'
|
||||
% release )
|
||||
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
|
||||
info( '*** RT Scheduler is not enabled in your kernel. '
|
||||
'Skipping this test\n' )
|
||||
continue
|
||||
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
|
||||
net = Mininet( topo=myTopo, intf=intf, host=host )
|
||||
net.start()
|
||||
testLinkLimit( net, bw=bw )
|
||||
net.runCpuLimitTest( cpu=cpu )
|
||||
net.stop()
|
||||
|
||||
def verySimpleLimit( bw=150 ):
|
||||
"Absurdly simple limiting test"
|
||||
intf = custom( TCIntf, bw=bw )
|
||||
net = Mininet( intf=intf )
|
||||
h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.iperf()
|
||||
h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() )
|
||||
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
|
||||
h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() )
|
||||
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
limit()
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Test bandwidth (using iperf) on linear networks of varying size,
|
||||
using both kernel and user datapaths.
|
||||
|
||||
We construct a network of N hosts and N-1 switches, connected as follows:
|
||||
|
||||
h1 <-> s1 <-> s2 .. sN-1
|
||||
| | |
|
||||
h2 h3 hN
|
||||
|
||||
WARNING: by default, the reference controller only supports 16
|
||||
switches, so this test WILL NOT WORK unless you have recompiled
|
||||
your controller to support 100 switches (or more.)
|
||||
|
||||
In addition to testing the bandwidth across varying numbers
|
||||
of switches, this example demonstrates:
|
||||
|
||||
- creating a custom topology, LinearTestTopo
|
||||
- using the ping() and iperf() tests from Mininet()
|
||||
- testing both the kernel and user switches
|
||||
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
|
||||
from mininet.topo import Topo
|
||||
from mininet.log import lg
|
||||
from mininet.util import irange, quietRun
|
||||
from mininet.link import TCLink
|
||||
from functools import partial
|
||||
|
||||
import sys
|
||||
flush = sys.stdout.flush
|
||||
|
||||
class LinearTestTopo( Topo ):
|
||||
"Topology for a string of N hosts and N-1 switches."
|
||||
|
||||
def __init__( self, N, **params ):
|
||||
|
||||
# Initialize topology
|
||||
Topo.__init__( self, **params )
|
||||
|
||||
# Create switches and hosts
|
||||
hosts = [ self.addHost( 'h%s' % h )
|
||||
for h in irange( 1, N ) ]
|
||||
switches = [ self.addSwitch( 's%s' % s )
|
||||
for s in irange( 1, N - 1 ) ]
|
||||
|
||||
# Wire up switches
|
||||
last = None
|
||||
for switch in switches:
|
||||
if last:
|
||||
self.addLink( last, switch )
|
||||
last = switch
|
||||
|
||||
# Wire up hosts
|
||||
self.addLink( hosts[ 0 ], switches[ 0 ] )
|
||||
for host, switch in zip( hosts[ 1: ], switches ):
|
||||
self.addLink( host, switch )
|
||||
|
||||
|
||||
def linearBandwidthTest( lengths ):
|
||||
|
||||
"Check bandwidth at various lengths along a switch chain."
|
||||
|
||||
results = {}
|
||||
switchCount = max( lengths )
|
||||
hostCount = switchCount + 1
|
||||
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
# UserSwitch is horribly slow with recent kernels.
|
||||
# We can reinstate it once its performance is fixed
|
||||
del switches[ 'reference user' ]
|
||||
|
||||
topo = LinearTestTopo( hostCount )
|
||||
|
||||
# Select TCP Reno
|
||||
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
|
||||
assert 'reno' in output
|
||||
|
||||
for datapath in switches.keys():
|
||||
print "*** testing", datapath, "datapath"
|
||||
Switch = switches[ datapath ]
|
||||
results[ datapath ] = []
|
||||
link = partial( TCLink, delay='1ms' )
|
||||
net = Mininet( topo=topo, switch=Switch,
|
||||
controller=Controller, waitConnected=True,
|
||||
link=link )
|
||||
net.start()
|
||||
print "*** testing basic connectivity"
|
||||
for n in lengths:
|
||||
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
|
||||
print "*** testing bandwidth"
|
||||
for n in lengths:
|
||||
src, dst = net.hosts[ 0 ], net.hosts[ n ]
|
||||
# Try to prime the pump to reduce PACKET_INs during test
|
||||
# since the reference controller is reactive
|
||||
src.cmd( 'telnet', dst.IP(), '5001' )
|
||||
print "testing", src.name, "<->", dst.name,
|
||||
bandwidth = net.iperf( [ src, dst ], seconds=10 )
|
||||
print bandwidth
|
||||
flush()
|
||||
results[ datapath ] += [ ( n, bandwidth ) ]
|
||||
net.stop()
|
||||
|
||||
for datapath in switches.keys():
|
||||
print
|
||||
print "*** Linear network results for", datapath, "datapath:"
|
||||
print
|
||||
result = results[ datapath ]
|
||||
print "SwitchCount\tiperf Results"
|
||||
for switchCount, bandwidth in result:
|
||||
print switchCount, '\t\t',
|
||||
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
|
||||
print
|
||||
print
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info' )
|
||||
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
|
||||
print "*** Running linearBandwidthTest", sizes
|
||||
linearBandwidthTest( sizes )
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
linuxrouter.py: Example network with Linux IP router
|
||||
|
||||
This example converts a Node into a router using IP forwarding
|
||||
already built into Linux.
|
||||
|
||||
The example topology creates a router and three IP subnets:
|
||||
|
||||
- 192.168.1.0/24 (r0-eth1, IP: 192.168.1.1)
|
||||
- 172.16.0.0/12 (r0-eth2, IP: 172.16.0.1)
|
||||
- 10.0.0.0/8 (r0-eth3, IP: 10.0.0.1)
|
||||
|
||||
Each subnet consists of a single host connected to
|
||||
a single switch:
|
||||
|
||||
r0-eth1 - s1-eth1 - h1-eth0 (IP: 192.168.1.100)
|
||||
r0-eth2 - s2-eth1 - h2-eth0 (IP: 172.16.0.100)
|
||||
r0-eth3 - s3-eth1 - h3-eth0 (IP: 10.0.0.100)
|
||||
|
||||
The example relies on default routing entries that are
|
||||
automatically created for each router interface, as well
|
||||
as 'defaultRoute' parameters for the host interfaces.
|
||||
|
||||
Additional routes may be added to the router or hosts by
|
||||
executing 'ip route' or 'route' commands on the router or hosts.
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
|
||||
class LinuxRouter( Node ):
|
||||
"A Node with IP forwarding enabled."
|
||||
|
||||
def config( self, **params ):
|
||||
super( LinuxRouter, self).config( **params )
|
||||
# Enable forwarding on the router
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
||||
|
||||
def terminate( self ):
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
super( LinuxRouter, self ).terminate()
|
||||
|
||||
|
||||
class NetworkTopo( Topo ):
|
||||
"A LinuxRouter connecting three IP subnets"
|
||||
|
||||
def build( self, **_opts ):
|
||||
|
||||
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
|
||||
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
|
||||
|
||||
s1, s2, s3 = [ self.addSwitch( s ) for s in 's1', 's2', 's3' ]
|
||||
|
||||
self.addLink( s1, router, intfName2='r0-eth1',
|
||||
params2={ 'ip' : defaultIP } ) # for clarity
|
||||
self.addLink( s2, router, intfName2='r0-eth2',
|
||||
params2={ 'ip' : '172.16.0.1/12' } )
|
||||
self.addLink( s3, router, intfName2='r0-eth3',
|
||||
params2={ 'ip' : '10.0.0.1/8' } )
|
||||
|
||||
h1 = self.addHost( 'h1', ip='192.168.1.100/24',
|
||||
defaultRoute='via 192.168.1.1' )
|
||||
h2 = self.addHost( 'h2', ip='172.16.0.100/12',
|
||||
defaultRoute='via 172.16.0.1' )
|
||||
h3 = self.addHost( 'h3', ip='10.0.0.100/8',
|
||||
defaultRoute='via 10.0.0.1' )
|
||||
|
||||
for h, s in [ (h1, s1), (h2, s2), (h3, s3) ]:
|
||||
self.addLink( h, s )
|
||||
|
||||
|
||||
def run():
|
||||
"Test linux router"
|
||||
topo = NetworkTopo()
|
||||
net = Mininet( topo=topo ) # controller is used by s1-s3
|
||||
net.start()
|
||||
info( '*** Routing Table on Router:\n' )
|
||||
print net[ 'r0' ].cmd( 'route' )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
run()
|
||||
Executable
+3584
File diff suppressed because it is too large.
Load diff
@@ -1,49 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2020, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
ndn = Minindn()
|
||||
|
||||
ndn.start()
|
||||
|
||||
info('Starting NFD on nodes\n')
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
info('Starting NLSR on nodes\n')
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Simple example of Mobility with Mininet
|
||||
(aka enough rope to hang yourself.)
|
||||
|
||||
We move a host from s1 to s2, s2 to s3, and then back to s1.
|
||||
|
||||
Gotchas:
|
||||
|
||||
The reference controller doesn't support mobility, so we need to
|
||||
manually flush the switch flow tables!
|
||||
|
||||
Good luck!
|
||||
|
||||
to-do:
|
||||
|
||||
- think about wifi/hub behavior
|
||||
- think about clearing last hop - why doesn't that work?
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import OVSSwitch
|
||||
from mininet.topo import LinearTopo
|
||||
from mininet.log import output, warn
|
||||
|
||||
from random import randint
|
||||
|
||||
|
||||
class MobilitySwitch( OVSSwitch ):
|
||||
"Switch that can reattach and rename interfaces"
|
||||
|
||||
def delIntf( self, intf ):
|
||||
"Remove (and detach) an interface"
|
||||
port = self.ports[ intf ]
|
||||
del self.ports[ intf ]
|
||||
del self.intfs[ port ]
|
||||
del self.nameToIntf[ intf.name ]
|
||||
|
||||
def addIntf( self, intf, rename=False, **kwargs ):
|
||||
"Add (and reparent) an interface"
|
||||
OVSSwitch.addIntf( self, intf, **kwargs )
|
||||
intf.node = self
|
||||
if rename:
|
||||
self.renameIntf( intf )
|
||||
|
||||
def attach( self, intf ):
|
||||
"Attach an interface and set its port"
|
||||
port = self.ports[ intf ]
|
||||
if port:
|
||||
if self.isOldOVS():
|
||||
self.cmd( 'ovs-vsctl add-port', self, intf )
|
||||
else:
|
||||
self.cmd( 'ovs-vsctl add-port', self, intf,
|
||||
'-- set Interface', intf,
|
||||
'ofport_request=%s' % port )
|
||||
self.validatePort( intf )
|
||||
|
||||
def validatePort( self, intf ):
|
||||
"Validate intf's OF port number"
|
||||
ofport = int( self.cmd( 'ovs-vsctl get Interface', intf,
|
||||
'ofport' ) )
|
||||
if ofport != self.ports[ intf ]:
|
||||
warn( 'WARNING: ofport for', intf, 'is actually', ofport,
|
||||
'\n' )
|
||||
|
||||
def renameIntf( self, intf, newname='' ):
|
||||
"Rename an interface (to its canonical name)"
|
||||
intf.ifconfig( 'down' )
|
||||
if not newname:
|
||||
newname = '%s-eth%d' % ( self.name, self.ports[ intf ] )
|
||||
intf.cmd( 'ip link set', intf, 'name', newname )
|
||||
del self.nameToIntf[ intf.name ]
|
||||
intf.name = newname
|
||||
self.nameToIntf[ intf.name ] = intf
|
||||
intf.ifconfig( 'up' )
|
||||
|
||||
def moveIntf( self, intf, switch, port=None, rename=True ):
|
||||
"Move one of our interfaces to another switch"
|
||||
self.detach( intf )
|
||||
self.delIntf( intf )
|
||||
switch.addIntf( intf, port=port, rename=rename )
|
||||
switch.attach( intf )
|
||||
|
||||
|
||||
def printConnections( switches ):
|
||||
"Compactly print connected nodes to each switch"
|
||||
for sw in switches:
|
||||
output( '%s: ' % sw )
|
||||
for intf in sw.intfList():
|
||||
link = intf.link
|
||||
if link:
|
||||
intf1, intf2 = link.intf1, link.intf2
|
||||
remote = intf1 if intf1.node != sw else intf2
|
||||
output( '%s(%s) ' % ( remote.node, sw.ports[ intf ] ) )
|
||||
output( '\n' )
|
||||
|
||||
|
||||
def moveHost( host, oldSwitch, newSwitch, newPort=None ):
|
||||
"Move a host from old switch to new switch"
|
||||
hintf, sintf = host.connectionsTo( oldSwitch )[ 0 ]
|
||||
oldSwitch.moveIntf( sintf, newSwitch, port=newPort )
|
||||
return hintf, sintf
|
||||
|
||||
|
||||
def mobilityTest():
|
||||
"A simple test of mobility"
|
||||
print '* Simple mobility test'
|
||||
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
|
||||
print '* Starting network:'
|
||||
net.start()
|
||||
printConnections( net.switches )
|
||||
print '* Testing network'
|
||||
net.pingAll()
|
||||
print '* Identifying switch interface for h1'
|
||||
h1, old = net.get( 'h1', 's1' )
|
||||
for s in 2, 3, 1:
|
||||
new = net[ 's%d' % s ]
|
||||
port = randint( 10, 20 )
|
||||
print '* Moving', h1, 'from', old, 'to', new, 'port', port
|
||||
hintf, sintf = moveHost( h1, old, new, newPort=port )
|
||||
print '*', hintf, 'is now connected to', sintf
|
||||
print '* Clearing out old flows'
|
||||
for sw in net.switches:
|
||||
sw.dpctl( 'del-flows' )
|
||||
print '* New network:'
|
||||
printConnections( net.switches )
|
||||
print '* Testing connectivity:'
|
||||
net.pingAll()
|
||||
old = new
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
mobilityTest()
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This is a simple example that demonstrates multiple links
|
||||
between nodes.
|
||||
"""
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import Topo
|
||||
|
||||
def runMultiLink():
|
||||
"Create and run multiple link network"
|
||||
topo = simpleMultiLinkTopo( n=2 )
|
||||
net = Mininet( topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
class simpleMultiLinkTopo( Topo ):
|
||||
"Simple topology with multiple links"
|
||||
|
||||
def __init__( self, n, **kwargs ):
|
||||
Topo.__init__( self, **kwargs )
|
||||
|
||||
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
|
||||
s1 = self.addSwitch( 's1' )
|
||||
|
||||
for _ in range( n ):
|
||||
self.addLink( s1, h1 )
|
||||
self.addLink( s1, h2 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
runMultiLink()
|
||||
Executable
+275
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Multiple ovsdb OVS!!
|
||||
|
||||
We scale up by creating multiple ovsdb instances,
|
||||
each of which is shared by several OVS switches
|
||||
|
||||
The shell may also be shared among switch instances,
|
||||
which causes switch.cmd() and switch.popen() to be
|
||||
delegated to the ovsdb instance.
|
||||
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node, OVSSwitch
|
||||
from mininet.node import OVSBridge
|
||||
from mininet.link import Link, OVSIntf
|
||||
from mininet.topo import LinearTopo, SingleSwitchTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
from mininet.clean import Cleanup, sh
|
||||
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
|
||||
class OVSDB( Node ):
|
||||
"Namespace for an OVSDB instance"
|
||||
|
||||
privateDirs = [ '/etc/openvswitch',
|
||||
'/var/run/openvswitch',
|
||||
'/var/log/openvswitch' ]
|
||||
|
||||
# Control network
|
||||
ipBase = '172.123.123.0/24'
|
||||
cnet = None
|
||||
nat = None
|
||||
|
||||
@classmethod
|
||||
def startControlNet( cls ):
|
||||
"Start control net if necessary and return it"
|
||||
cnet = cls.cnet
|
||||
if not cnet:
|
||||
info( '### Starting control network\n' )
|
||||
cnet = Mininet( ipBase=cls.ipBase )
|
||||
cswitch = cnet.addSwitch( 'ovsbr0', cls=OVSBridge )
|
||||
# Add NAT - note this can conflict with data network NAT
|
||||
info( '### Adding NAT for control and data networks'
|
||||
' (use --nat flush=0 for data network)\n' )
|
||||
cls.cnet = cnet
|
||||
cls.nat = cnet.addNAT( 'ovsdbnat0')
|
||||
cnet.start()
|
||||
info( '### Control network started\n' )
|
||||
return cnet
|
||||
|
||||
def stopControlNet( self ):
|
||||
info( '\n### Stopping control network\n' )
|
||||
cls = self.__class__
|
||||
cls.cnet.stop()
|
||||
info( '### Control network stopped\n' )
|
||||
|
||||
def addSwitch( self, switch ):
|
||||
"Add a switch to our namespace"
|
||||
# Attach first switch to cswitch!
|
||||
self.switches.append( switch )
|
||||
|
||||
def delSwitch( self, switch ):
|
||||
"Delete a switch from our namespace, and terminate if none left"
|
||||
self.switches.remove( switch )
|
||||
if not self.switches:
|
||||
self.stopOVS()
|
||||
|
||||
ovsdbCount = 0
|
||||
|
||||
def startOVS( self ):
|
||||
"Start new OVS instance"
|
||||
self.cmd( 'ovsdb-tool create /etc/openvswitch/conf.db' )
|
||||
self.cmd( 'ovsdb-server /etc/openvswitch/conf.db'
|
||||
' -vfile:emer -vfile:err -vfile:info'
|
||||
' --remote=punix:/var/run/openvswitch/db.sock '
|
||||
' --log-file=/var/log/openvswitch/ovsdb-server.log'
|
||||
' --pidfile=/var/run/openvswitch/ovsdb-server-mn.pid'
|
||||
' --no-chdir'
|
||||
' --detach' )
|
||||
|
||||
self.cmd( 'ovs-vswitchd unix:/var/run/openvswitch/db.sock'
|
||||
' -vfile:emer -vfile:err -vfile:info'
|
||||
' --mlockall --log-file=/var/log/openvswitch/ovs-vswitchd.log'
|
||||
' --pidfile=/var/run/openvswitch/ovs-vswitchd-mn.pid'
|
||||
' --no-chdir'
|
||||
' --detach' )
|
||||
|
||||
def stopOVS( self ):
|
||||
self.cmd( 'kill',
|
||||
'`cat /var/run/openvswitch/ovs-vswitchd-mn.pid`',
|
||||
'`cat /var/run/openvswitch/ovsdb-server-mn.pid`' )
|
||||
self.cmd( 'wait' )
|
||||
self.__class__.ovsdbCount -= 1
|
||||
if self.__class__.ovsdbCount <= 0:
|
||||
self.stopControlNet()
|
||||
|
||||
@classmethod
|
||||
def cleanUpOVS( cls ):
|
||||
"Clean up leftover ovsdb-server/ovs-vswitchd processes"
|
||||
info( '*** Shutting down extra ovsdb-server/ovs-vswitchd processes\n' )
|
||||
sh( 'pkill -f mn.pid' )
|
||||
|
||||
def self( self, *args, **kwargs ):
|
||||
"A fake constructor that sets params and returns self"
|
||||
self.params = kwargs
|
||||
return self
|
||||
|
||||
def __init__( self, **kwargs ):
|
||||
cls = self.__class__
|
||||
cls.ovsdbCount += 1
|
||||
cnet = self.startControlNet()
|
||||
# Create a new ovsdb namespace
|
||||
self.switches = []
|
||||
name = 'ovsdb%d' % cls.ovsdbCount
|
||||
kwargs.update( inNamespace=True )
|
||||
kwargs.setdefault( 'privateDirs', self.privateDirs )
|
||||
super( OVSDB, self ).__init__( name, **kwargs )
|
||||
ovsdb = cnet.addHost( name, cls=self.self, **kwargs )
|
||||
link = cnet.addLink( ovsdb, cnet.switches[ 0 ] )
|
||||
cnet.switches[ 0 ].attach( link.intf2 )
|
||||
ovsdb.configDefault()
|
||||
ovsdb.setDefaultRoute( 'via %s' % self.nat.intfs[ 0 ].IP() )
|
||||
ovsdb.startOVS()
|
||||
|
||||
|
||||
# Install cleanup callback
|
||||
Cleanup.addCleanupCallback( OVSDB.cleanUpOVS )
|
||||
|
||||
|
||||
class OVSSwitchNS( OVSSwitch ):
|
||||
"OVS Switch in shared OVSNS namespace"
|
||||
|
||||
isSetup = False
|
||||
|
||||
@classmethod
|
||||
def batchStartup( cls, switches ):
|
||||
result = []
|
||||
for ovsdb, switchGroup in groupby( switches, attrgetter( 'ovsdb') ):
|
||||
switchGroup = list( switchGroup )
|
||||
info( '(%s)' % ovsdb )
|
||||
result += OVSSwitch.batchStartup( switchGroup, run=ovsdb.cmd )
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def batchShutdown( cls, switches ):
|
||||
result = []
|
||||
for ovsdb, switchGroup in groupby( switches, attrgetter( 'ovsdb') ):
|
||||
switchGroup = list( switchGroup )
|
||||
info( '(%s)' % ovsdb )
|
||||
for switch in switches:
|
||||
if switch.pid == ovsdb.pid:
|
||||
switch.pid = None
|
||||
switch.shell = None
|
||||
result += OVSSwitch.batchShutdown( switchGroup, run=ovsdb.cmd )
|
||||
for switch in switchGroup:
|
||||
switch.ovsdbFree()
|
||||
return result
|
||||
|
||||
# OVSDB allocation
|
||||
groupSize = 64
|
||||
switchCount = 0
|
||||
lastOvsdb = None
|
||||
|
||||
@classmethod
|
||||
def ovsdbAlloc( cls, switch ):
|
||||
"Allocate (possibly new) OVSDB instance for switch"
|
||||
if cls.switchCount % switch.groupSize == 0:
|
||||
cls.lastOvsdb = OVSDB()
|
||||
cls.switchCount += 1
|
||||
cls.lastOvsdb.addSwitch( switch )
|
||||
return cls.lastOvsdb
|
||||
|
||||
def ovsdbFree( self ):
|
||||
"Deallocate OVSDB instance for switch"
|
||||
self.ovsdb.delSwitch( self )
|
||||
|
||||
def startShell( self, *args, **kwargs ):
|
||||
"Start shell in shared OVSDB namespace"
|
||||
ovsdb = self.ovsdbAlloc( self )
|
||||
kwargs.update( mnopts='-da %d ' % ovsdb.pid )
|
||||
self.ns = [ 'net' ]
|
||||
self.ovsdb = ovsdb
|
||||
self._waiting = False
|
||||
if self.privateShell:
|
||||
super( OVSSwitchNS, self ).startShell( *args, **kwargs )
|
||||
else:
|
||||
# Delegate methods and initialize local vars
|
||||
attrs = ( 'cmd', 'cmdPrint', 'sendCmd', 'waitOutput',
|
||||
'monitor', 'write', 'read',
|
||||
'pid', 'shell', 'stdout',)
|
||||
for attr in attrs:
|
||||
setattr( self, attr, getattr( ovsdb, attr ) )
|
||||
self.defaultIntf().updateIP()
|
||||
|
||||
@property
|
||||
def waiting( self ):
|
||||
"Optionally delegated to ovsdb"
|
||||
return self._waiting if self.privateShell else self.ovsdb.waiting
|
||||
|
||||
@waiting.setter
|
||||
def waiting( self, value ):
|
||||
"Optionally delegated to ovsdb (read only!)"
|
||||
if self.privateShell:
|
||||
_waiting = value
|
||||
|
||||
def start( self, controllers ):
|
||||
"Update controller IP addresses if necessary"
|
||||
for controller in controllers:
|
||||
if controller.IP() == '127.0.0.1' and not controller.intfs:
|
||||
controller.intfs[ 0 ] = self.ovsdb.nat.intfs[ 0 ]
|
||||
super( OVSSwitchNS, self ).start( controllers )
|
||||
|
||||
def stop( self, *args, **kwargs ):
|
||||
"Stop and free OVSDB namespace if necessary"
|
||||
self.ovsdbFree()
|
||||
|
||||
def terminate( self, *args, **kwargs ):
|
||||
if self.privateShell:
|
||||
super( OVSSwitchNS, self ).terminate( *args, **kwargs )
|
||||
else:
|
||||
self.pid = None
|
||||
self.shell= None
|
||||
|
||||
def defaultIntf( self ):
|
||||
return self.ovsdb.defaultIntf()
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"""n: number of OVS instances per OVSDB
|
||||
shell: run private shell/bash process? (False)
|
||||
If shell is shared/not private, cmd() and popen() are
|
||||
delegated to the OVSDB instance, which is different than
|
||||
regular OVSSwitch semantics!!"""
|
||||
self.groupSize = kwargs.pop( 'n', self.groupSize )
|
||||
self.privateShell = kwargs.pop( 'shell', False )
|
||||
super( OVSSwitchNS, self ).__init__( *args, **kwargs )
|
||||
|
||||
|
||||
class OVSLinkNS( Link ):
|
||||
"OVSLink that supports OVSSwitchNS"
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"See Link.__init__() for options"
|
||||
self.isPatchLink = False
|
||||
if ( isinstance( node1, OVSSwitch ) and
|
||||
isinstance( node2, OVSSwitch ) and
|
||||
getattr( node1, 'ovsdb', None ) ==
|
||||
getattr( node2, 'ovsdb', None ) ):
|
||||
self.isPatchLink = True
|
||||
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
switches = { 'ovsns': OVSSwitchNS, 'ovsm': OVSSwitchNS }
|
||||
|
||||
links = { 'ovs': OVSLinkNS }
|
||||
|
||||
def test():
|
||||
"Test OVSNS switch"
|
||||
setLogLevel( 'info' )
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
net = Mininet( topo=topo, switch=OVSSwitchNS )
|
||||
# Add connectivity to controller which is on LAN or in root NS
|
||||
# net.addNAT().configDefault()
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
multiping.py: monitor multiple sets of hosts using ping
|
||||
|
||||
This demonstrates how one may send a simple shell script to
|
||||
multiple hosts and monitor their output interactively for a period=
|
||||
of time.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from select import poll, POLLIN
|
||||
from time import time
|
||||
|
||||
def chunks( l, n ):
|
||||
"Divide list l into chunks of size n - thanks Stackoverflow"
|
||||
return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
|
||||
|
||||
def startpings( host, targetips ):
|
||||
"Tell host to repeatedly ping targets"
|
||||
|
||||
targetips = ' '.join( targetips )
|
||||
|
||||
# Simple ping loop
|
||||
cmd = ( 'while true; do '
|
||||
' for ip in %s; do ' % targetips +
|
||||
' echo -n %s "->" $ip ' % host.IP() +
|
||||
' `ping -c1 -w 1 $ip | grep packets` ;'
|
||||
' sleep 1;'
|
||||
' done; '
|
||||
'done &' )
|
||||
|
||||
print ( '*** Host %s (%s) will be pinging ips: %s' %
|
||||
( host.name, host.IP(), targetips ) )
|
||||
|
||||
host.cmd( cmd )
|
||||
|
||||
def multiping( netsize, chunksize, seconds):
|
||||
"Ping subsets of size chunksize in net of size netsize"
|
||||
|
||||
# Create network and identify subnets
|
||||
topo = SingleSwitchTopo( netsize )
|
||||
net = Mininet( topo=topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
subnets = chunks( hosts, chunksize )
|
||||
|
||||
# Create polling object
|
||||
fds = [ host.stdout.fileno() for host in hosts ]
|
||||
poller = poll()
|
||||
for fd in fds:
|
||||
poller.register( fd, POLLIN )
|
||||
|
||||
# Start pings
|
||||
for subnet in subnets:
|
||||
ips = [ host.IP() for host in subnet ]
|
||||
#adding bogus to generate packet loss
|
||||
ips.append( '10.0.0.200' )
|
||||
for host in subnet:
|
||||
startpings( host, ips )
|
||||
|
||||
# Monitor output
|
||||
endTime = time() + seconds
|
||||
while time() < endTime:
|
||||
readable = poller.poll(1000)
|
||||
for fd, _mask in readable:
|
||||
node = Node.outToNode[ fd ]
|
||||
print '%s:' % node.name, node.monitor().strip()
|
||||
|
||||
# Stop pings
|
||||
for host in hosts:
|
||||
host.cmd( 'kill %while' )
|
||||
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
multiping( netsize=20, chunksize=4, seconds=10 )
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Simple example of sending output to multiple files and
|
||||
monitoring them
|
||||
"""
|
||||
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from time import time
|
||||
from select import poll, POLLIN
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
"Monitor set of files and return [(host, line)...]"
|
||||
devnull = open( '/dev/null', 'w' )
|
||||
tails, fdToFile, fdToHost = {}, {}, {}
|
||||
for h, outfile in outfiles.iteritems():
|
||||
tail = Popen( [ 'tail', '-f', outfile ],
|
||||
stdout=PIPE, stderr=devnull )
|
||||
fd = tail.stdout.fileno()
|
||||
tails[ h ] = tail
|
||||
fdToFile[ fd ] = tail.stdout
|
||||
fdToHost[ fd ] = h
|
||||
# Prepare to poll output files
|
||||
readable = poll()
|
||||
for t in tails.values():
|
||||
readable.register( t.stdout.fileno(), POLLIN )
|
||||
# Run until a set number of seconds have elapsed
|
||||
endTime = time() + seconds
|
||||
while time() < endTime:
|
||||
fdlist = readable.poll(timeoutms)
|
||||
if fdlist:
|
||||
for fd, _flags in fdlist:
|
||||
f = fdToFile[ fd ]
|
||||
host = fdToHost[ fd ]
|
||||
# Wait for a line of output
|
||||
line = f.readline().strip()
|
||||
yield host, line
|
||||
else:
|
||||
# If we timed out, return nothing
|
||||
yield None, ''
|
||||
for t in tails.values():
|
||||
t.terminate()
|
||||
devnull.close() # Not really necessary
|
||||
|
||||
|
||||
def monitorTest( N=3, seconds=3 ):
|
||||
"Run pings and monitor multiple hosts"
|
||||
topo = SingleSwitchTopo( N )
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
outfiles, errfiles = {}, {}
|
||||
for h in hosts:
|
||||
# Create and/or erase output files
|
||||
outfiles[ h ] = '/tmp/%s.out' % h.name
|
||||
errfiles[ h ] = '/tmp/%s.err' % h.name
|
||||
h.cmd( 'echo >', outfiles[ h ] )
|
||||
h.cmd( 'echo >', errfiles[ h ] )
|
||||
# Start pings
|
||||
h.cmdPrint('ping', server.IP(),
|
||||
'>', outfiles[ h ],
|
||||
'2>', errfiles[ h ],
|
||||
'&' )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
|
||||
if h:
|
||||
print '%s: %s' % ( h.name, line )
|
||||
for h in hosts:
|
||||
h.cmd('kill %ping')
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
monitorTest()
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example shows how to create a network and run multiple tests.
|
||||
For a more complicated test example, see udpbwtest.py.
|
||||
"""
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, info
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import OVSKernelSwitch
|
||||
from mininet.topolib import TreeTopo
|
||||
|
||||
def ifconfigTest( net ):
|
||||
"Run ifconfig on all hosts in net."
|
||||
hosts = net.hosts
|
||||
for host in hosts:
|
||||
info( host.cmd( 'ifconfig' ) )
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info' )
|
||||
info( "*** Initializing Mininet and kernel modules\n" )
|
||||
OVSKernelSwitch.setup()
|
||||
info( "*** Creating network\n" )
|
||||
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
|
||||
info( "*** Starting network\n" )
|
||||
network.start()
|
||||
info( "*** Running ping test\n" )
|
||||
network.pingAll()
|
||||
info( "*** Running ifconfig test\n" )
|
||||
ifconfigTest( network )
|
||||
info( "*** Starting CLI (type 'exit' to exit)\n" )
|
||||
CLI( network )
|
||||
info( "*** Stopping network\n" )
|
||||
network.stop()
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Example to create a Mininet topology and connect it to the internet via NAT
|
||||
through eth0 on the host.
|
||||
|
||||
Glen Gibb, February 2011
|
||||
|
||||
(slight modifications by BL, 5/13)
|
||||
"""
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg
|
||||
from mininet.node import Node
|
||||
from mininet.topolib import TreeNet
|
||||
|
||||
#################################
|
||||
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
|
||||
"""Start NAT/forwarding between Mininet and external network
|
||||
root: node to access iptables from
|
||||
inetIntf: interface for internet access
|
||||
subnet: Mininet subnet (default 10.0/8)="""
|
||||
|
||||
# Identify the interface connecting to the mininet network
|
||||
localIntf = root.defaultIntf()
|
||||
|
||||
# Flush any currently active rules
|
||||
root.cmd( 'iptables -F' )
|
||||
root.cmd( 'iptables -t nat -F' )
|
||||
|
||||
# Create default entries for unmatched traffic
|
||||
root.cmd( 'iptables -P INPUT ACCEPT' )
|
||||
root.cmd( 'iptables -P OUTPUT ACCEPT' )
|
||||
root.cmd( 'iptables -P FORWARD DROP' )
|
||||
|
||||
# Configure NAT
|
||||
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
|
||||
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
|
||||
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
|
||||
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
|
||||
|
||||
# Instruct the kernel to perform forwarding
|
||||
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
||||
|
||||
def stopNAT( root ):
|
||||
"""Stop NAT/forwarding between Mininet and external network"""
|
||||
# Flush any currently active rules
|
||||
root.cmd( 'iptables -F' )
|
||||
root.cmd( 'iptables -t nat -F' )
|
||||
|
||||
# Instruct the kernel to stop forwarding
|
||||
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
def fixNetworkManager( root, intf ):
|
||||
"""Prevent network-manager from messing with our interface,
|
||||
by specifying manual configuration in /etc/network/interfaces
|
||||
root: a node in the root namespace (for running commands)
|
||||
intf: interface name"""
|
||||
cfile = '/etc/network/interfaces'
|
||||
line = '\niface %s inet manual\n' % intf
|
||||
config = open( cfile ).read()
|
||||
if line not in config:
|
||||
print '*** Adding', line.strip(), 'to', cfile
|
||||
with open( cfile, 'a' ) as f:
|
||||
f.write( line )
|
||||
# Probably need to restart network-manager to be safe -
|
||||
# hopefully this won't disconnect you
|
||||
root.cmd( 'service network-manager restart' )
|
||||
|
||||
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
|
||||
"""Connect the network to the internet
|
||||
switch: switch to connect to root namespace
|
||||
rootip: address for interface in root namespace
|
||||
subnet: Mininet subnet"""
|
||||
switch = network.get( switch )
|
||||
prefixLen = subnet.split( '/' )[ 1 ]
|
||||
|
||||
# Create a node in root namespace
|
||||
root = Node( 'root', inNamespace=False )
|
||||
|
||||
# Prevent network-manager from interfering with our interface
|
||||
fixNetworkManager( root, 'root-eth0' )
|
||||
|
||||
# Create link between root NS and switch
|
||||
link = network.addLink( root, switch )
|
||||
link.intf1.setIP( rootip, prefixLen )
|
||||
|
||||
# Start network that now includes link to root namespace
|
||||
network.start()
|
||||
|
||||
# Start NAT and establish forwarding
|
||||
startNAT( root )
|
||||
|
||||
# Establish routes from end hosts
|
||||
for host in network.hosts:
|
||||
host.cmd( 'ip route flush root 0/0' )
|
||||
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
|
||||
host.cmd( 'route add default gw', rootip )
|
||||
|
||||
return root
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4 )
|
||||
# Configure and start NATted connectivity
|
||||
rootnode = connectToInternet( net )
|
||||
print "*** Hosts are running and should have internet connectivity"
|
||||
print "*** Type 'exit' or control-D to shut down network"
|
||||
CLI( net )
|
||||
# Shut down NAT
|
||||
stopNAT( rootnode )
|
||||
net.stop()
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
natnet.py: Example network with NATs
|
||||
|
||||
|
||||
h0
|
||||
|
|
||||
s0
|
||||
|
|
||||
----------------
|
||||
| |
|
||||
nat1 nat2
|
||||
| |
|
||||
s1 s2
|
||||
| |
|
||||
h1 h2
|
||||
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.nodelib import NAT
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.cli import CLI
|
||||
from mininet.util import irange
|
||||
|
||||
class InternetTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def __init__(self, n=2, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
|
||||
# set up inet switch
|
||||
inetSwitch = self.addSwitch('s0')
|
||||
# add inet host
|
||||
inetHost = self.addHost('h0')
|
||||
self.addLink(inetSwitch, inetHost)
|
||||
|
||||
# add local nets
|
||||
for i in irange(1, n):
|
||||
inetIntf = 'nat%d-eth0' % i
|
||||
localIntf = 'nat%d-eth1' % i
|
||||
localIP = '192.168.%d.1' % i
|
||||
localSubnet = '192.168.%d.0/24' % i
|
||||
natParams = { 'ip' : '%s/24' % localIP }
|
||||
# add NAT to topology
|
||||
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
|
||||
inetIntf=inetIntf, localIntf=localIntf)
|
||||
switch = self.addSwitch('s%d' % i)
|
||||
# connect NAT to inet and local switches
|
||||
self.addLink(nat, inetSwitch, intfName1=inetIntf)
|
||||
self.addLink(nat, switch, intfName1=localIntf, params1=natParams)
|
||||
# add host and connect to local switch
|
||||
host = self.addHost('h%d' % i,
|
||||
ip='192.168.%d.100/24' % i,
|
||||
defaultRoute='via %s' % localIP)
|
||||
self.addLink(host, switch)
|
||||
|
||||
def run():
|
||||
"Create network and run the CLI"
|
||||
topo = InternetTopo()
|
||||
net = Mininet(topo=topo)
|
||||
net.start()
|
||||
CLI(net)
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
run()
|
||||
@@ -1,79 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2019, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.topo import Topo
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
|
||||
from nlsr_common import getParser
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
topo = Topo()
|
||||
h1 = topo.addHost('h1')
|
||||
h2 = topo.addHost('h2')
|
||||
topo.addLink(h1, h2, delay='10ms')
|
||||
|
||||
ndn = Minindn(parser=getParser(), topo=topo)
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, [], Nlsr)
|
||||
|
||||
host1 = ndn.net.hosts[0]
|
||||
nlsrs.startOnNode(host1, security=args.security, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType)
|
||||
|
||||
|
||||
expectedTotalCount = 500
|
||||
for i in range(0, expectedTotalCount):
|
||||
host1.cmd('nlsrc advertise /long/name/to/exceed/max/packet/size/host1/{}'.format(i))
|
||||
|
||||
time.sleep(60)
|
||||
|
||||
host2 = ndn.net.hosts[1]
|
||||
nlsrs.startOnNode(host2, security=args.security, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType)
|
||||
|
||||
time.sleep(60)
|
||||
|
||||
advertiseCount = int(host2.cmd('nfdc fib | grep host1 | wc -l'))
|
||||
info(advertiseCount)
|
||||
if advertiseCount == expectedTotalCount:
|
||||
info('\nSuccessfully advertised {} prefixes\n'.format(expectedTotalCount))
|
||||
else:
|
||||
info('\nAdvertising {} prefixes failed. Exiting...\n'.format(expectedTotalCount))
|
||||
ndn.stop()
|
||||
sys.exit(1)
|
||||
|
||||
ndn.stop()
|
||||
@@ -1,64 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2019, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.experiment import Experiment
|
||||
|
||||
from nlsr_common import getParser
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
ndn = Minindn(parser=getParser())
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, [], Nlsr)
|
||||
|
||||
i = 1
|
||||
info('Starting NLSR on nodes\n')
|
||||
for host in ndn.net.hosts:
|
||||
nlsrs.startOnNode(host, security=args.security, sync=args.sync, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType)
|
||||
|
||||
# Wait 1/2 minute between starting NLSRs
|
||||
# Wait 1 hour before starting last NLSR
|
||||
if i == len(ndn.net.hosts) - 1:
|
||||
info('Sleeping 1 hour before starting last NLSR\n')
|
||||
time.sleep(3600)
|
||||
else:
|
||||
time.sleep(30)
|
||||
i += 1
|
||||
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
|
||||
|
||||
ndn.stop()
|
||||
@@ -1,90 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.experiment import Experiment
|
||||
from minindn.helpers.nfdc import Nfdc
|
||||
from minindn.helpers.ndnping import NDNPing
|
||||
|
||||
from nlsr_common import getParser
|
||||
|
||||
def mcnFailure(ndn, nfds, nlsrs, args):
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
|
||||
if args.nPings != 0:
|
||||
Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE)
|
||||
pingedDict = Experiment.startPctPings(ndn.net, args.nPings, args.pctTraffic)
|
||||
|
||||
PING_COLLECTION_TIME_BEFORE_FAILURE = 60
|
||||
PING_COLLECTION_TIME_AFTER_RECOVERY = 120
|
||||
|
||||
time.sleep(PING_COLLECTION_TIME_BEFORE_FAILURE)
|
||||
|
||||
mcn = max(ndn.net.hosts, key=lambda host: len(host.intfNames()))
|
||||
|
||||
info('Bringing down node {}\n'.format(mcn.name))
|
||||
nlsrs[mcn.name].stop()
|
||||
nfds[mcn.name].stop()
|
||||
|
||||
time.sleep(args.ctime)
|
||||
|
||||
info('Bringing up node {}\n'.format(mcn.name))
|
||||
nfds[mcn.name].start()
|
||||
nlsrs[mcn.name].start()
|
||||
|
||||
# Restart pings
|
||||
if args.nPings != 0:
|
||||
Experiment.setupPing([mcn], Nfdc.STRATEGY_BEST_ROUTE)
|
||||
for nodeToPing in pingedDict[mcn]:
|
||||
NDNPing.ping(mcn, nodeToPing, nPings=PING_COLLECTION_TIME_AFTER_RECOVERY)
|
||||
|
||||
time.sleep(PING_COLLECTION_TIME_AFTER_RECOVERY)
|
||||
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
ndn = Minindn(parser=getParser())
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
|
||||
security=args.security, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType)
|
||||
|
||||
mcnFailure(ndn, nfds, nlsrs, args)
|
||||
|
||||
if args.isCliEnabled:
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
@@ -1,109 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.experiment import Experiment
|
||||
from minindn.helpers.nfdc import Nfdc
|
||||
from minindn.helpers.ndnping import NDNPing
|
||||
|
||||
from nlsr_common import getParser
|
||||
|
||||
def multipleFailure(ndn, nfds, nlsrs, args):
|
||||
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
|
||||
Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE)
|
||||
|
||||
PING_COLLECTION_TIME_BEFORE_FAILURE = 60
|
||||
FAILURE_INTERVAL = 60
|
||||
RECOVERY_INTERVAL = 60
|
||||
|
||||
# Number of pings required to make it through the full experiment
|
||||
nInitialPings = (PING_COLLECTION_TIME_BEFORE_FAILURE +
|
||||
len(ndn.net.hosts) * (FAILURE_INTERVAL + RECOVERY_INTERVAL))
|
||||
print('Scheduling with {} initial pings'.format(nInitialPings))
|
||||
|
||||
pingedDict = Experiment.startPctPings(ndn.net, nInitialPings, args.pctTraffic)
|
||||
time.sleep(PING_COLLECTION_TIME_BEFORE_FAILURE)
|
||||
|
||||
nNodesRemainingToFail = len(ndn.net.hosts)
|
||||
|
||||
for host in ndn.net.hosts:
|
||||
# Fail the node
|
||||
info('Bringing down node {}\n'.format(host.name))
|
||||
nlsrs[host.name].stop()
|
||||
nfds[host.name].stop()
|
||||
|
||||
# Stay in failure state for FAILURE_INTERVAL seconds
|
||||
time.sleep(FAILURE_INTERVAL)
|
||||
|
||||
# Bring the node back up
|
||||
start_time = time.time()
|
||||
info('Bringing up node {}\n'.format(host.name))
|
||||
nfds[host.name].start()
|
||||
nlsrs[host.name].start()
|
||||
Experiment.setupPing([host], Nfdc.STRATEGY_BEST_ROUTE)
|
||||
|
||||
recovery_time = int(time.time() - start_time)
|
||||
|
||||
# Number of pings required to reach the end of the test
|
||||
nNodesRemainingToFail -= 1
|
||||
nPings = ((RECOVERY_INTERVAL - recovery_time) +
|
||||
nNodesRemainingToFail * (FAILURE_INTERVAL + RECOVERY_INTERVAL))
|
||||
|
||||
info('Scheduling with {} remaining pings\n'.format(nPings))
|
||||
|
||||
# Restart pings
|
||||
for nodeToPing in pingedDict[host]:
|
||||
NDNPing.ping(host, nodeToPing, nPings=nPings)
|
||||
|
||||
time.sleep(RECOVERY_INTERVAL - recovery_time)
|
||||
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
ndn = Minindn(parser=getParser())
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
|
||||
security=args.security, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType)
|
||||
|
||||
multipleFailure(ndn, nfds, nlsrs, args)
|
||||
|
||||
if args.isCliEnabled:
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
@@ -1,57 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2019, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
|
||||
def getParser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--ctime', type=int, default=60,
|
||||
help='Specify convergence time for the topology (Default: 60 seconds)')
|
||||
|
||||
parser.add_argument('--faces', type=int, default=3,
|
||||
help='Specify number of max faces per prefix for NLSR 0-60')
|
||||
|
||||
parser.add_argument('--routing', dest='routingType', default='link-state',
|
||||
choices=['link-state', 'hr', 'dry'],
|
||||
help='''Choose routing type, dry = link-state is used
|
||||
but hr is calculated for comparision.''')
|
||||
|
||||
parser.add_argument('--sync', dest='sync', default='psync',
|
||||
choices=['chronosync', 'psync'],
|
||||
help='choose the sync protocol to be used by NLSR.')
|
||||
|
||||
parser.add_argument('--security', action='store_true', dest='security',
|
||||
help='Enables NLSR security')
|
||||
|
||||
parser.add_argument('--face-type', dest='faceType', default='udp', choices=['udp', 'tcp'])
|
||||
|
||||
parser.add_argument('--no-cli', action='store_false', dest='isCliEnabled',
|
||||
help='Run experiments and exit without showing the command line interface')
|
||||
|
||||
parser.add_argument('--pct-traffic', dest='pctTraffic', type=float, default=1.0,
|
||||
help='Specify the percentage of nodes each node should ping')
|
||||
|
||||
parser.add_argument('--nPings', type=int, default=300,
|
||||
help='Number of pings to perform between each node in the experiment')
|
||||
|
||||
return parser
|
||||
@@ -1,63 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2019, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.experiment import Experiment
|
||||
from minindn.helpers.nfdc import Nfdc
|
||||
|
||||
from nlsr_common import getParser
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
ndn = Minindn(parser=getParser())
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
|
||||
security=args.security, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType,
|
||||
logLevel='ndn.*=TRACE:nlsr.*=TRACE')
|
||||
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=False)
|
||||
|
||||
if args.nPings != 0:
|
||||
Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE)
|
||||
Experiment.startPctPings(ndn.net, args.nPings, args.pctTraffic)
|
||||
|
||||
time.sleep(args.nPings + 10)
|
||||
|
||||
if args.isCliEnabled:
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
@@ -1,85 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.helpers.experiment import Experiment
|
||||
|
||||
from nlsr_common import getParser
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
ndn = Minindn(parser=getParser())
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
|
||||
security=args.security, faceType=args.faceType,
|
||||
nFaces=args.faces, routingType=args.routingType)
|
||||
|
||||
Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
|
||||
|
||||
firstNode = ndn.net.hosts[0]
|
||||
|
||||
if args.security:
|
||||
firstNode.cmd('ndnsec-set-default /ndn/{}-site/%C1.Operator/op'.format(firstNode.name))
|
||||
|
||||
info('Testing advertise\n')
|
||||
firstNode.cmd('nlsrc advertise /testPrefix')
|
||||
time.sleep(30)
|
||||
|
||||
for host in ndn.net.hosts:
|
||||
if host.name != firstNode.name:
|
||||
if (int(host.cmd('nfdc fib | grep testPrefix | wc -l')) != 1 or
|
||||
int(host.cmd('nlsrc status | grep testPrefix | wc -l')) != 1):
|
||||
info('Advertise test failed\n')
|
||||
ndn.stop()
|
||||
sys.exit(1)
|
||||
|
||||
info('Testing withdraw\n')
|
||||
firstNode.cmd('nlsrc withdraw /testPrefix')
|
||||
time.sleep(30)
|
||||
|
||||
for host in ndn.net.hosts:
|
||||
if host.name != firstNode.name:
|
||||
if (int(host.cmd('nfdc fib | grep testPrefix | wc -l')) != 0 or
|
||||
int(host.cmd('nlsrc status | grep testPrefix | wc -l')) != 0):
|
||||
info('Withdraw test failed\n')
|
||||
ndn.stop()
|
||||
sys.exit(1)
|
||||
|
||||
if args.isCliEnabled:
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Create a network with 5 hosts, numbered 1-4 and 9.
|
||||
Validate that the port numbers match to the interface name,
|
||||
and that the ovs ports match the mininet ports.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller
|
||||
from mininet.log import setLogLevel, info, warn
|
||||
|
||||
def validatePort( switch, intf ):
|
||||
"Validate intf's OF port number"
|
||||
ofport = int( switch.cmd( 'ovs-vsctl get Interface', intf,
|
||||
'ofport' ) )
|
||||
if ofport != switch.ports[ intf ]:
|
||||
warn( 'WARNING: ofport for', intf, 'is actually', ofport, '\n' )
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def testPortNumbering():
|
||||
|
||||
"""Test port numbering:
|
||||
Create a network with 5 hosts (using Mininet's
|
||||
mid-level API) and check that implicit and
|
||||
explicit port numbering works as expected."""
|
||||
|
||||
net = Mininet( controller=Controller )
|
||||
|
||||
info( '*** Adding controller\n' )
|
||||
net.addController( 'c0' )
|
||||
|
||||
info( '*** Adding hosts\n' )
|
||||
h1 = net.addHost( 'h1', ip='10.0.0.1' )
|
||||
h2 = net.addHost( 'h2', ip='10.0.0.2' )
|
||||
h3 = net.addHost( 'h3', ip='10.0.0.3' )
|
||||
h4 = net.addHost( 'h4', ip='10.0.0.4' )
|
||||
h5 = net.addHost( 'h5', ip='10.0.0.5' )
|
||||
|
||||
info( '*** Adding switch\n' )
|
||||
s1 = net.addSwitch( 's1' )
|
||||
|
||||
info( '*** Creating links\n' )
|
||||
# host 1-4 connect to ports 1-4 on the switch
|
||||
net.addLink( h1, s1 )
|
||||
net.addLink( h2, s1 )
|
||||
net.addLink( h3, s1 )
|
||||
net.addLink( h4, s1 )
|
||||
# specify a different port to connect host 5 to on the switch.
|
||||
net.addLink( h5, s1, port1=1, port2= 9)
|
||||
|
||||
info( '*** Starting network\n' )
|
||||
net.start()
|
||||
|
||||
# print the interfaces and their port numbers
|
||||
info( '\n*** printing and validating the ports '
|
||||
'running on each interface\n' )
|
||||
for intfs in s1.intfList():
|
||||
if not intfs.name == "lo":
|
||||
info( intfs, ': ', s1.ports[intfs],
|
||||
'\n' )
|
||||
info( 'Validating that', intfs,
|
||||
'is actually on port', s1.ports[intfs], '... ' )
|
||||
if validatePort( s1, intfs ):
|
||||
info( 'Validated.\n' )
|
||||
print '\n'
|
||||
|
||||
# test the network with pingall
|
||||
net.pingAll()
|
||||
print '\n'
|
||||
|
||||
info( '*** Stopping network' )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testPortNumbering()
|
||||
@@ -1,53 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2020, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.apps.nlsr import Nlsr
|
||||
from minindn.apps.tshark import Tshark
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
ndn = Minindn()
|
||||
|
||||
ndn.start()
|
||||
|
||||
info('Starting tshark logging on nodes\n')
|
||||
tshark = AppManager(ndn, ndn.net.hosts, Tshark, logFolder="./log/", singleLogFile=False)
|
||||
|
||||
info('Starting NFD on nodes\n')
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
info('Starting NLSR on nodes\n')
|
||||
nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
|
||||
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
@@ -1,121 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from subprocess import PIPE
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.topo import Topo
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.util import MiniNDNCLI, getPopen
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.helpers.nfdc import Nfdc
|
||||
|
||||
PREFIX = "/example"
|
||||
|
||||
def printOutput(output):
|
||||
_out = output.decode("utf-8").split("\n")
|
||||
for _line in _out:
|
||||
info(_line + "\n")
|
||||
|
||||
def run():
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
# Topology can be created/modified using Mininet topo object
|
||||
topo = Topo()
|
||||
info("Setup\n")
|
||||
# add hosts
|
||||
a = topo.addHost('a')
|
||||
b = topo.addHost('b')
|
||||
c = topo.addHost('c')
|
||||
|
||||
# add links
|
||||
topo.addLink(a, b, delay='10ms', bw=2.5) # bw = bandwidth
|
||||
topo.addLink(b, c, delay='10ms', bw=2.5)
|
||||
|
||||
info(topo.links(withInfo=True))
|
||||
|
||||
ndn = Minindn(topo=topo)
|
||||
ndn.start()
|
||||
|
||||
# configure and start nfd on each node
|
||||
info("Configuring NFD111\n")
|
||||
AppManager(ndn, ndn.net.hosts, Nfd, logLevel="DEBUG")
|
||||
|
||||
"""
|
||||
There are multiple ways of setting up routes in Mini-NDN
|
||||
refer: https://minindn.memphis.edu/experiment.html#routing-options
|
||||
It can also be set manually as follows. The important bit to note here
|
||||
is the use of the Nfdc command
|
||||
"""
|
||||
for link in topo.links(withInfo=True):
|
||||
node1, node2, node_info = link
|
||||
host1 = ndn.net[node1]
|
||||
host2 = ndn.net[node2]
|
||||
interface = host2.connectionsTo(host1)[0][0]
|
||||
interface_ip = interface.IP()
|
||||
bandwidth = interface.params.get("bw", 100) * 1000000
|
||||
info(f"Setting up route from {node1} to {node2} with bandwidth {bandwidth}\n")
|
||||
Nfdc.createFace(host1, interface_ip, bandwidth=bandwidth)
|
||||
Nfdc.registerRoute(host1, PREFIX, interface_ip, cost=0)
|
||||
# links = {"a":["b"], "b":["c"]}
|
||||
# for first in links:
|
||||
# for second in links[first]:
|
||||
# host1 = ndn.net[first]
|
||||
# host2 = ndn.net[second]
|
||||
# interface = host2.connectionsTo(host1)[0][0]
|
||||
# interface_ip = interface.IP()
|
||||
# Nfdc.createFace(host1, interface_ip)
|
||||
# Nfdc.registerRoute(host1, PREFIX, interface_ip, cost=0)
|
||||
|
||||
# Start ping server
|
||||
info("Starting pings...\n")
|
||||
pingserver_log = open("{}/c/ndnpingserver.log".format(ndn.workDir), "w")
|
||||
getPopen(ndn.net["c"], "ndnpingserver {}".format(PREFIX), stdout=pingserver_log,\
|
||||
stderr=pingserver_log)
|
||||
|
||||
# start ping client
|
||||
ping1 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE)
|
||||
ping1.wait()
|
||||
printOutput(ping1.stdout.read())
|
||||
|
||||
interface = ndn.net["b"].connectionsTo(ndn.net["a"])[0][0]
|
||||
info("Failing link\n") # failing link by setting link loss to 100%
|
||||
interface.config(delay="10ms", bw=10, loss=100)
|
||||
info ("\n starting ping2 client \n")
|
||||
|
||||
ping2 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE)
|
||||
ping2.wait()
|
||||
printOutput(ping2.stdout.read())
|
||||
|
||||
interface.config(delay="10ms", bw=10, loss=0) # bringing back the link
|
||||
|
||||
info("\nExperiment Completed!\n")
|
||||
MiniNDNCLI(ndn.net)
|
||||
ndn.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel("info")
|
||||
run()
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example monitors a number of hosts using host.popen() and
|
||||
pmonitor()
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import custom, pmonitor
|
||||
|
||||
def monitorhosts( hosts=5, sched='cfs' ):
|
||||
"Start a bunch of pings and monitor them using popen"
|
||||
mytopo = SingleSwitchTopo( hosts )
|
||||
cpu = .5 / hosts
|
||||
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
|
||||
net = Mininet( topo=mytopo, host=myhost )
|
||||
net.start()
|
||||
# Start a bunch of pings
|
||||
popens = {}
|
||||
last = net.hosts[ -1 ]
|
||||
for host in net.hosts:
|
||||
popens[ host ] = host.popen( "ping -c5 %s" % last.IP() )
|
||||
last = host
|
||||
# Monitor them and print output
|
||||
for host, line in pmonitor( popens ):
|
||||
if host:
|
||||
print "<%s>: %s" % ( host.name, line.strip() )
|
||||
# Done
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
monitorhosts( hosts=5 )
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"Monitor multiple hosts using popen()/pmonitor()"
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.util import pmonitor
|
||||
from time import time
|
||||
from signal import SIGINT
|
||||
|
||||
def pmonitorTest( N=3, seconds=10 ):
|
||||
"Run pings and monitor multiple hosts using pmonitor"
|
||||
topo = SingleSwitchTopo( N )
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
popens = {}
|
||||
for h in hosts:
|
||||
popens[ h ] = h.popen('ping', server.IP() )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
endTime = time() + seconds
|
||||
for h, line in pmonitor( popens, timeoutms=500 ):
|
||||
if h:
|
||||
print '<%s>: %s' % ( h.name, line ),
|
||||
if time() >= endTime:
|
||||
for p in popens.values():
|
||||
p.send_signal( SIGINT )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pmonitorTest()
|
||||
@@ -1,81 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2019, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.helpers.nfdc import Nfdc
|
||||
|
||||
def registerRouteToAllNeighbors(ndn, host, syncPrefix):
|
||||
for node in ndn.net.hosts:
|
||||
for neighbor in node.connectionsTo(host):
|
||||
ip = node.IP(neighbor[0])
|
||||
faceID = Nfdc.createFace(host, ip)
|
||||
Nfdc.registerRoute(host, syncPrefix, faceID)
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
ndn = Minindn()
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
|
||||
syncPrefix = "/sync"
|
||||
numUserPrefixesPerNode = 2
|
||||
maxUpdatesPerUserPrefixPerNode = 3
|
||||
|
||||
for host in ndn.net.hosts:
|
||||
Nfdc.setStrategy(host, syncPrefix, Nfdc.STRATEGY_MULTICAST)
|
||||
registerRouteToAllNeighbors(ndn, host, syncPrefix)
|
||||
|
||||
info('Starting psync-full-sync on all the nodes\n')
|
||||
for host in ndn.net.hosts:
|
||||
host.cmd('export NDN_LOG=examples.FullSyncApp=INFO')
|
||||
host.cmd('psync-full-sync {} {} {} {} &> psync.logs &'
|
||||
.format(syncPrefix, host.name, numUserPrefixesPerNode,
|
||||
maxUpdatesPerUserPrefixPerNode))
|
||||
|
||||
info('Sleeping 5 minutes for convergence\n')
|
||||
# Estimated time for 4 node default topology
|
||||
time.sleep(300)
|
||||
|
||||
totalUpdates = int(host.cmd('grep -r Update {}/*/psync.logs | wc -l'
|
||||
.format(ndn.workDir)))
|
||||
|
||||
expectedUpdates = (maxUpdatesPerUserPrefixPerNode *
|
||||
len(ndn.net.hosts) * (len(ndn.net.hosts) - 1) * numUserPrefixesPerNode)
|
||||
|
||||
if totalUpdates == expectedUpdates:
|
||||
info('PSync full sync has successfully converged.\n')
|
||||
else:
|
||||
info('PSync full sync convergence was not successful. Exiting...\n')
|
||||
ndn.stop()
|
||||
sys.exit(1)
|
||||
@@ -1,67 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2019, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.topo import Topo
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
topo = Topo()
|
||||
topo.addHost('h1')
|
||||
|
||||
ndn = Minindn(topo=topo)
|
||||
args = ndn.args
|
||||
|
||||
ndn.start()
|
||||
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
|
||||
host1 = ndn.net.hosts[0]
|
||||
host1.cmd('export NDN_LOG=examples.PartialSyncProducerApp=INFO')
|
||||
host1.cmd('psync-producer /sync /{} 10 1 &> producer.log &'.format(host1.name))
|
||||
time.sleep(1)
|
||||
|
||||
host1.cmd('export NDN_LOG=examples.PartialSyncConsumerApp=INFO:$NDN_LOG')
|
||||
host1.cmd('psync-consumer /sync 5 &> consumer.log &')
|
||||
|
||||
info('Sleeping 90 seconds for convergence\n')
|
||||
time.sleep(90)
|
||||
|
||||
consumerSubs = int(host1.cmd('cat consumer.log | grep -c Subscribing'))
|
||||
consumerUpdates = int(host1.cmd('cat consumer.log | grep -c Update'))
|
||||
producerPublish = int(host1.cmd('cat producer.log | grep -c Publish'))
|
||||
|
||||
if consumerSubs == 5 and consumerUpdates == 5 and producerPublish == 10:
|
||||
info('PSync partial sync has successfully converged.\n')
|
||||
else:
|
||||
info('PSync partial sync convergence was not successful. Exiting...\n')
|
||||
ndn.stop()
|
||||
sys.exit(1)
|
||||
@@ -1,111 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2021, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from subprocess import PIPE
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.topo import Topo
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.util import MiniNDNCLI, getPopen
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.helpers.nfdc import Nfdc
|
||||
|
||||
PREFIX = "/A"
|
||||
|
||||
def printOutput(output):
|
||||
_out = output.decode("utf-8").split("\n")
|
||||
for _line in _out:
|
||||
info(_line + "\n")
|
||||
|
||||
def run():
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
# Topology can be created/modified using Mininet topo object
|
||||
topo = Topo()
|
||||
info("Setup\n")
|
||||
# add hosts
|
||||
a = topo.addHost('a')
|
||||
b = topo.addHost('b')
|
||||
|
||||
# add links
|
||||
topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth
|
||||
|
||||
info(topo.links(withInfo=True))
|
||||
|
||||
ndn = Minindn(topo=topo)
|
||||
ndn.start()
|
||||
|
||||
# configure and start nfd on each node
|
||||
info("Configuring NFD\n")
|
||||
AppManager(ndn, ndn.net.hosts, Nfd, logLevel="INFO")
|
||||
|
||||
"""
|
||||
There are multiple ways of setting up routes in Mini-NDN
|
||||
refer: https://minindn.memphis.edu/experiment.html#routing-options
|
||||
It can also be set manually as follows. The important bit to note here
|
||||
is the use of the Nfdc command
|
||||
"""
|
||||
for link in topo.links(withInfo=True):
|
||||
node1, node2, node_info = link
|
||||
host1 = ndn.net[node1]
|
||||
host2 = ndn.net[node2]
|
||||
interface = host2.connectionsTo(host1)[0][0]
|
||||
interface_ip = interface.IP()
|
||||
bandwidth = interface.params.get("bw", 100) * 1000000
|
||||
info(f"Setting up route from {node1} to {node2} with bandwidth {bandwidth}\n")
|
||||
Nfdc.createFace(host1, interface_ip, bandwidth=bandwidth)
|
||||
Nfdc.registerRoute(host1, PREFIX, interface_ip, cost=0)
|
||||
|
||||
# Start cc server
|
||||
info("Starting cc...\n")
|
||||
qsccp_server_log = open(f"{ndn.workDir}/qsccp-demo/qsccp-server.log", "w")
|
||||
getPopen(ndn.net["b"], "cc-producer --prefix {}".format(PREFIX), stdout=qsccp_server_log,\
|
||||
stderr=qsccp_server_log)
|
||||
|
||||
# start cc client
|
||||
qsccp_client_log = open(f"{ndn.workDir}/qsccp-demo/qsccp-client.log", "w")
|
||||
ping1 = getPopen(ndn.net["a"], "qsccp-client --prefix {} --timingStop 10000".format(PREFIX), stdout=qsccp_client_log, stderr=qsccp_client_log)
|
||||
ping1.wait()
|
||||
# printOutput(ping1.stdout.read())
|
||||
|
||||
# interface = ndn.net["b"].connectionsTo(ndn.net["a"])[0][0]
|
||||
# info("Failing link\n") # failing link by setting link loss to 100%
|
||||
# interface.config(delay="10ms", bw=10, loss=100)
|
||||
# info ("\n starting ping2 client \n")
|
||||
|
||||
# ping2 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE)
|
||||
# ping2.wait()
|
||||
# printOutput(ping2.stdout.read())
|
||||
|
||||
# interface.config(delay="10ms", bw=10, loss=0) # bringing back the link
|
||||
|
||||
info("\nExperiment Completed!\n")
|
||||
MiniNDNCLI(ndn.net)
|
||||
ndn.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel("info")
|
||||
run()
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Build a simple network from scratch, using mininet primitives.
|
||||
This is more complicated than using the higher-level classes,
|
||||
but it exposes the configuration details and allows customization.
|
||||
|
||||
For most tasks, the higher-level API will be preferable.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.link import Link
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.util import quietRun
|
||||
|
||||
from time import sleep
|
||||
|
||||
def scratchNet( cname='controller', cargs='-v ptcp:' ):
|
||||
"Create network from scratch using Open vSwitch."
|
||||
|
||||
info( "*** Creating nodes\n" )
|
||||
controller = Node( 'c0', inNamespace=False )
|
||||
switch = Node( 's0', inNamespace=False )
|
||||
h0 = Node( 'h0' )
|
||||
h1 = Node( 'h1' )
|
||||
|
||||
info( "*** Creating links\n" )
|
||||
Link( h0, switch )
|
||||
Link( h1, switch )
|
||||
|
||||
info( "*** Configuring hosts\n" )
|
||||
h0.setIP( '192.168.123.1/24' )
|
||||
h1.setIP( '192.168.123.2/24' )
|
||||
info( str( h0 ) + '\n' )
|
||||
info( str( h1 ) + '\n' )
|
||||
|
||||
info( "*** Starting network using Open vSwitch\n" )
|
||||
controller.cmd( cname + ' ' + cargs + '&' )
|
||||
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||
switch.cmd( 'ovs-vsctl add-br dp0' )
|
||||
for intf in switch.intfs.values():
|
||||
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
|
||||
|
||||
# Note: controller and switch are in root namespace, and we
|
||||
# can connect via loopback interface
|
||||
switch.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:6633' )
|
||||
|
||||
info( '*** Waiting for switch to connect to controller' )
|
||||
while 'is_connected' not in quietRun( 'ovs-vsctl show' ):
|
||||
sleep( 1 )
|
||||
info( '.' )
|
||||
info( '\n' )
|
||||
|
||||
info( "*** Running test\n" )
|
||||
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
|
||||
|
||||
info( "*** Stopping network\n" )
|
||||
controller.cmd( 'kill %' + cname )
|
||||
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||
switch.deleteIntfs()
|
||||
info( '\n' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
info( '*** Scratch network demo (kernel datapath)\n' )
|
||||
Mininet.init()
|
||||
scratchNet()
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Build a simple network from scratch, using mininet primitives.
|
||||
This is more complicated than using the higher-level classes,
|
||||
but it exposes the configuration details and allows customization.
|
||||
|
||||
For most tasks, the higher-level API will be preferable.
|
||||
|
||||
This version uses the user datapath and an explicit control network.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.link import Link
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
def linkIntfs( node1, node2 ):
|
||||
"Create link from node1 to node2 and return intfs"
|
||||
link = Link( node1, node2 )
|
||||
return link.intf1, link.intf2
|
||||
|
||||
def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
"Create network from scratch using user switch."
|
||||
|
||||
# It's not strictly necessary for the controller and switches
|
||||
# to be in separate namespaces. For performance, they probably
|
||||
# should be in the root namespace. However, it's interesting to
|
||||
# see how they could work even if they are in separate namespaces.
|
||||
|
||||
info( '*** Creating Network\n' )
|
||||
controller = Node( 'c0' )
|
||||
switch = Node( 's0')
|
||||
h0 = Node( 'h0' )
|
||||
h1 = Node( 'h1' )
|
||||
cintf, sintf = linkIntfs( controller, switch )
|
||||
h0intf, sintf1 = linkIntfs( h0, switch )
|
||||
h1intf, sintf2 = linkIntfs( h1, switch )
|
||||
|
||||
info( '*** Configuring control network\n' )
|
||||
controller.setIP( '10.0.123.1/24', intf=cintf )
|
||||
switch.setIP( '10.0.123.2/24', intf=sintf)
|
||||
|
||||
info( '*** Configuring hosts\n' )
|
||||
h0.setIP( '192.168.123.1/24', intf=h0intf )
|
||||
h1.setIP( '192.168.123.2/24', intf=h1intf )
|
||||
|
||||
info( '*** Network state:\n' )
|
||||
for node in controller, switch, h0, h1:
|
||||
info( str( node ) + '\n' )
|
||||
|
||||
info( '*** Starting controller and user datapath\n' )
|
||||
controller.cmd( cname + ' ' + cargs + '&' )
|
||||
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
||||
intfs = [ str( i ) for i in sintf1, sintf2 ]
|
||||
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
|
||||
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
|
||||
|
||||
info( '*** Running test\n' )
|
||||
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
|
||||
|
||||
info( '*** Stopping network\n' )
|
||||
controller.cmd( 'kill %' + cname )
|
||||
switch.cmd( 'kill %ofdatapath' )
|
||||
switch.cmd( 'kill %ofprotocol' )
|
||||
switch.deleteIntfs()
|
||||
info( '\n' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
info( '*** Scratch network demo (user datapath)\n' )
|
||||
Mininet.init()
|
||||
scratchNetUser()
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Simple example of setting network and CPU parameters
|
||||
|
||||
NOTE: link params limit BW, add latency, and loss.
|
||||
There is a high chance that pings WILL fail and that
|
||||
iperf will hang indefinitely if the TCP handshake fails
|
||||
to complete.
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.link import TCLink
|
||||
from mininet.util import dumpNodeConnections
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
class SingleSwitchTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def __init__(self, n=2, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
switch = self.addSwitch('s1')
|
||||
for h in range(n):
|
||||
# Each host gets 50%/n of system CPU
|
||||
host = self.addHost('h%s' % (h + 1),
|
||||
cpu=.5 / n)
|
||||
# 10 Mbps, 5ms delay, 10% loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
|
||||
def perfTest():
|
||||
"Create network and run simple performance test"
|
||||
topo = SingleSwitchTopo( n=4 )
|
||||
net = Mininet( topo=topo,
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
autoStaticArp=True )
|
||||
net.start()
|
||||
print "Dumping host connections"
|
||||
dumpNodeConnections(net.hosts)
|
||||
print "Testing bandwidth between h1 and h4"
|
||||
h1, h4 = net.getNodeByName('h1', 'h4')
|
||||
net.iperf( ( h1, h4 ), l4Type='UDP' )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
perfTest()
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Create a network and start sshd(8) on each host.
|
||||
|
||||
While something like rshd(8) would be lighter and faster,
|
||||
(and perfectly adequate on an in-machine network)
|
||||
the advantage of running sshd is that scripts can work
|
||||
unchanged on mininet and hardware.
|
||||
|
||||
In addition to providing ssh access to hosts, this example
|
||||
demonstrates:
|
||||
|
||||
- creating a convenience function to construct networks
|
||||
- connecting the host network to the root namespace
|
||||
- running server processes (sshd in this case) on hosts
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg
|
||||
from mininet.node import Node
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import waitListening
|
||||
|
||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||
"Convenience function for creating tree networks."
|
||||
topo = TreeTopo( depth, fanout )
|
||||
return Mininet( topo, **kwargs )
|
||||
|
||||
def connectToRootNS( network, switch, ip, routes ):
|
||||
"""Connect hosts to root namespace via switch. Starts network.
|
||||
network: Mininet() network object
|
||||
switch: switch to connect to root namespace
|
||||
ip: IP address for root namespace node
|
||||
routes: host networks to route to"""
|
||||
# Create a node in root namespace and link to switch 0
|
||||
root = Node( 'root', inNamespace=False )
|
||||
intf = network.addLink( root, switch ).intf1
|
||||
root.setIP( ip, intf=intf )
|
||||
# Start network that now includes link to root namespace
|
||||
network.start()
|
||||
# Add routes from root ns to hosts
|
||||
for route in routes:
|
||||
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
|
||||
|
||||
def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
|
||||
ip='10.123.123.1/32', routes=None, switch=None ):
|
||||
"""Start a network, connect it to root ns, and run sshd on all hosts.
|
||||
ip: root-eth0 IP address in root namespace (10.123.123.1/32)
|
||||
routes: Mininet host networks to route to (10.0/24)
|
||||
switch: Mininet switch to connect to root namespace (s1)"""
|
||||
if not switch:
|
||||
switch = network[ 's1' ] # switch to use
|
||||
if not routes:
|
||||
routes = [ '10.0.0.0/24' ]
|
||||
connectToRootNS( network, switch, ip, routes )
|
||||
for host in network.hosts:
|
||||
host.cmd( cmd + ' ' + opts + '&' )
|
||||
print "*** Waiting for ssh daemons to start"
|
||||
for server in network.hosts:
|
||||
waitListening( server=server, port=22, timeout=5 )
|
||||
|
||||
print
|
||||
print "*** Hosts are running sshd at the following addresses:"
|
||||
print
|
||||
for host in network.hosts:
|
||||
print host.name, host.IP()
|
||||
print
|
||||
print "*** Type 'exit' or control-D to shut down network"
|
||||
CLI( network )
|
||||
for host in network.hosts:
|
||||
host.cmd( 'kill %' + cmd )
|
||||
network.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4 )
|
||||
# get sshd args from the command line or use default args
|
||||
# useDNS=no -u0 to avoid reverse DNS lookup timeout
|
||||
argvopts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
|
||||
'-D -o UseDNS=no -u0' )
|
||||
sshd( net, opts=argvopts )
|
||||
@@ -1,100 +0,0 @@
|
||||
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
||||
#
|
||||
# Copyright (C) 2015-2020, The University of Memphis,
|
||||
# Arizona Board of Regents,
|
||||
# Regents of the University of California.
|
||||
#
|
||||
# This file is part of Mini-NDN.
|
||||
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
|
||||
#
|
||||
# Mini-NDN is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mini-NDN is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mini-NDN, e.g., in COPYING.md file.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.topo import Topo
|
||||
|
||||
from minindn.minindn import Minindn
|
||||
from minindn.util import MiniNDNCLI
|
||||
from minindn.apps.app_manager import AppManager
|
||||
from minindn.apps.nfd import Nfd
|
||||
from minindn.helpers.ndn_routing_helper import NdnRoutingHelper
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
|
||||
Minindn.cleanUp()
|
||||
Minindn.verifyDependencies()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--face-type', dest='faceType', default='udp', choices=['udp', 'tcp'])
|
||||
parser.add_argument('--routing', dest='routingType', default='link-state',
|
||||
choices=['link-state', 'hr', 'dry'],
|
||||
help='''Choose routing type, dry = link-state is used
|
||||
but hr is calculated for comparision.''')
|
||||
|
||||
'''
|
||||
Experiment run with default topology, test cases won't work with other topologies
|
||||
# With calculateNPossibleRoutes,
|
||||
10 # routing = hr, N = All, from A, 3 routes needs to added to NFD.
|
||||
a +++++++ b # a - b -- cost 10
|
||||
+ + # a - c -- cost 10
|
||||
10 + + 10 # a - d -- cost 20
|
||||
+ + # Same goes for B being a source.
|
||||
c d #
|
||||
'''
|
||||
topo = Topo()
|
||||
a = topo.addHost('a')
|
||||
b = topo.addHost('b')
|
||||
c = topo.addHost('c')
|
||||
d = topo.addHost('d')
|
||||
topo.addLink(a, b, delay='10ms')
|
||||
topo.addLink(a, c, delay='10ms')
|
||||
topo.addLink(b, d, delay='10ms')
|
||||
|
||||
ndn = Minindn(parser=parser, topo=topo)
|
||||
|
||||
ndn.start()
|
||||
|
||||
info('Starting NFD on nodes\n')
|
||||
nfds = AppManager(ndn, ndn.net.hosts, Nfd)
|
||||
|
||||
info('Adding static routes to NFD\n')
|
||||
grh = NdnRoutingHelper(ndn.net, ndn.args.faceType, ndn.args.routingType)
|
||||
# For all host, pass ndn.net.hosts or a list, [ndn.net['a'], ..] or [ndn.net.hosts[0],.]
|
||||
grh.addOrigin([ndn.net['a']], ["/abc"])
|
||||
grh.calculateNPossibleRoutes()
|
||||
|
||||
'''
|
||||
prefix "/abc" is advertise from node A, it should be reachable from all other nodes.
|
||||
'''
|
||||
routesFromA = ndn.net['a'].cmd("nfdc route | grep -v '/localhost/nfd'")
|
||||
if '/ndn/b-site/b' not in routesFromA or \
|
||||
'/ndn/c-site/c' not in routesFromA or \
|
||||
'/ndn/d-site/d' not in routesFromA:
|
||||
info("Route addition failed\n")
|
||||
|
||||
routesToPrefix = ndn.net['b'].cmd("nfdc fib | grep '/abc'")
|
||||
if '/abc' not in routesToPrefix:
|
||||
info("Missing route to advertised prefix, Route addition failed\n")
|
||||
ndn.net.stop()
|
||||
sys.exit(1)
|
||||
|
||||
info('Route addition to NFD completed\n')
|
||||
|
||||
MiniNDNCLI(ndn.net)
|
||||
|
||||
ndn.stop()
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Run all mininet.examples tests
|
||||
-v : verbose output
|
||||
-quick : skip tests that take more than ~30 seconds
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
from mininet.util import ensureRoot
|
||||
from mininet.clean import cleanup
|
||||
|
||||
class MininetTestResult( unittest.TextTestResult ):
|
||||
def addFailure( self, test, err ):
|
||||
super( MininetTestResult, self ).addFailure( test, err )
|
||||
cleanup()
|
||||
def addError( self,test, err ):
|
||||
super( MininetTestResult, self ).addError( test, err )
|
||||
cleanup()
|
||||
|
||||
class MininetTestRunner( unittest.TextTestRunner ):
|
||||
def _makeResult( self ):
|
||||
return MininetTestResult( self.stream, self.descriptions, self.verbosity )
|
||||
|
||||
def runTests( testDir, verbosity=1 ):
|
||||
"discover and run all tests in testDir"
|
||||
# ensure root and cleanup before starting tests
|
||||
ensureRoot()
|
||||
cleanup()
|
||||
# discover all tests in testDir
|
||||
testSuite = unittest.defaultTestLoader.discover( testDir )
|
||||
# run tests
|
||||
MininetTestRunner( verbosity=verbosity ).run( testSuite )
|
||||
|
||||
if __name__ == '__main__':
|
||||
# get the directory containing example tests
|
||||
testDir = os.path.dirname( os.path.realpath( __file__ ) )
|
||||
verbosity = 2 if '-v' in sys.argv else 1
|
||||
runTests( testDir, verbosity )
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for baresshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.clean import cleanup, sh
|
||||
|
||||
class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
opts = [ 'Welcome to h1', pexpect.EOF, pexpect.TIMEOUT ]
|
||||
|
||||
def connected( self ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def setUp( self ):
|
||||
# verify that sshd is not running
|
||||
self.assertFalse( self.connected() )
|
||||
# create public key pair for testing
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
sh( 'mkdir /tmp/ssh' )
|
||||
sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" )
|
||||
sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' )
|
||||
# run example with custom sshd args
|
||||
cmd = ( 'python -m mininet.examples.baresshd '
|
||||
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
|
||||
'-o StrictModes=no' )
|
||||
p = pexpect.spawn( cmd )
|
||||
runOpts = [ 'You may now ssh into h1 at 10.0.0.1',
|
||||
'after 5 seconds, h1 is not listening on port 22',
|
||||
pexpect.EOF, pexpect.TIMEOUT ]
|
||||
while True:
|
||||
index = p.expect( runOpts )
|
||||
if index == 0:
|
||||
break
|
||||
else:
|
||||
self.tearDown()
|
||||
self.fail( 'sshd failed to start in host h1' )
|
||||
|
||||
def testSSH( self ):
|
||||
"Simple test to verify that we can ssh into h1"
|
||||
result = False
|
||||
# try to connect up to 3 times; sshd can take a while to start
|
||||
result = self.connected()
|
||||
self.assertTrue( result )
|
||||
|
||||
def tearDown( self ):
|
||||
# kill the ssh process
|
||||
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
|
||||
cleanup()
|
||||
# remove public key pair
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for bind.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testBind( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def setUp( self ):
|
||||
self.net = pexpect.spawn( 'python -m mininet.examples.bind' )
|
||||
self.net.expect( "Private Directories: \[([\w\s,'/]+)\]" )
|
||||
self.directories = []
|
||||
# parse directories from mn output
|
||||
for d in self.net.match.group(1).split(', '):
|
||||
self.directories.append( d.strip("'") )
|
||||
self.net.expect( self.prompt )
|
||||
self.assertTrue( len( self.directories ) > 0 )
|
||||
|
||||
def testCreateFile( self ):
|
||||
"Create a file, a.txt, in the first private directory and verify"
|
||||
fileName = 'a.txt'
|
||||
directory = self.directories[ 0 ]
|
||||
path = directory + '/' + fileName
|
||||
self.net.sendline( 'h1 touch %s; ls %s' % ( path, directory ) )
|
||||
index = self.net.expect( [ fileName, self.prompt ] )
|
||||
self.assertTrue( index == 0 )
|
||||
self.net.expect( self.prompt )
|
||||
self.net.sendline( 'h1 rm %s' % path )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testIsolation( self ):
|
||||
"Create a file in two hosts and verify that contents are different"
|
||||
fileName = 'b.txt'
|
||||
directory = self.directories[ 0 ]
|
||||
path = directory + '/' + fileName
|
||||
contents = { 'h1' : '1', 'h2' : '2' }
|
||||
# Verify file doesn't exist, then write private copy of file
|
||||
for host in contents:
|
||||
value = contents[ host ]
|
||||
self.net.sendline( '%s cat %s' % ( host, path ) )
|
||||
self.net.expect( 'No such file' )
|
||||
self.net.expect( self.prompt )
|
||||
self.net.sendline( '%s echo %s > %s' % ( host, value, path ) )
|
||||
self.net.expect( self.prompt )
|
||||
# Verify file contents
|
||||
for host in contents:
|
||||
value = contents[ host ]
|
||||
self.net.sendline( '%s cat %s' % ( host, path ) )
|
||||
self.net.expect( value )
|
||||
self.net.expect( self.prompt )
|
||||
self.net.sendline( '%s rm %s' % ( host, path ) )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
# TODO: need more tests
|
||||
|
||||
def tearDown( self ):
|
||||
self.net.sendline( 'exit' )
|
||||
self.net.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
A simple sanity check test for cluster edition
|
||||
'''
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class clusterSanityCheck( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testClusterPingAll( self ):
|
||||
p = pexpect.spawn( 'python -m mininet.examples.clusterSanity' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect ( '(\d+)% dropped' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Loaded 100 of 220 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user