Compare commits
1666 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd0abe0e12 | |||
| 5a7b8f9833 | |||
| 1df1f9a1c5 | |||
| 125e669723 | |||
| a2486a6d66 | |||
| 15f2898f26 | |||
| 1c4adde1b6 | |||
| 4d22991245 | |||
| 90ea6c6bd1 | |||
| 269cecd3ee | |||
| c2be20f09e | |||
| df56fa2716 | |||
| f49e2539b7 | |||
| 4da7b3b7f0 | |||
| e5a5cd0070 | |||
| c5779deeb6 | |||
| 79c944aef4 | |||
| a23c6a2871 | |||
| 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 |
@@ -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,49 @@
|
||||
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
|
||||
Vitaly Ivanov
|
||||
Rich Lane
|
||||
Rémy Léone
|
||||
Zi Shen Lim
|
||||
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.1d2
|
||||
---
|
||||
|
||||
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.1d2 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,132 @@
|
||||
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.1d2
|
||||
|
||||
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.
|
||||
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
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!
|
||||
|
||||
The Mininet Core Team:
|
||||
|
||||
* Bob Lantz
|
||||
* Brian O'Connor
|
||||
* Cody Burkard
|
||||
|
||||
Thanks again to all of the Mininet contributors, particularly Gregory
|
||||
Gee for his work on MiniEdit.
|
||||
|
||||
and is available under `docs/_build/html`.
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
#!/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,
|
||||
UserSwitch, OVSSwitch, OVSBridge,
|
||||
IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink, OVSLink
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||
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': lambda: SingleSwitchTopo( k=2 ),
|
||||
'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': lambda name: None }
|
||||
|
||||
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, helpStr=None, **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
|
||||
helpStr: help string
|
||||
kwargs: additional arguments to add_option"""
|
||||
if not helpStr:
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
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' ):
|
||||
# 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="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)
|
||||
@@ -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
+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()
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for controllers.py and controllers2.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testControllers( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def connectedTest( self, name, cmap ):
|
||||
"Verify that switches are connected to the controller specified by cmap"
|
||||
p = pexpect.spawn( 'python -m %s' % name )
|
||||
p.expect( self.prompt )
|
||||
# but first a simple ping test
|
||||
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 )
|
||||
# verify connected controller
|
||||
for switch in cmap:
|
||||
p.sendline( 'sh ovs-vsctl get-controller %s' % switch )
|
||||
p.expect( 'tcp:([\d.:]+)')
|
||||
actual = p.match.group(1)
|
||||
expected = cmap[ switch ]
|
||||
self.assertEqual( actual, expected )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testControllers( self ):
|
||||
c0 = '127.0.0.1:6633'
|
||||
c1 = '127.0.0.1:6634'
|
||||
cmap = { 's1': c0, 's2': c1, 's3': c0 }
|
||||
self.connectedTest( 'mininet.examples.controllers', cmap )
|
||||
|
||||
def testControllers2( self ):
|
||||
c0 = '127.0.0.1:6633'
|
||||
c1 = '127.0.0.1:6634'
|
||||
cmap = { 's1': c0, 's2': c1 }
|
||||
self.connectedTest( 'mininet.examples.controllers2', cmap )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user