Compare commits
815 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02bf34aa96 | |||
| 13bdd914dc | |||
| 60b0c7a914 | |||
| 212399feaf | |||
| 08d83d136d | |||
| dd6424fee8 | |||
| bfdbb7089a | |||
| 16a384ab4b | |||
| a3e1a9a44d | |||
| 6a69c2f699 | |||
| db888fa5d4 | |||
| c9b844a721 | |||
| 604ad455ee | |||
| 8d493b686e | |||
| c0e7e34916 | |||
| 1a53141502 | |||
| 1285fb22dc | |||
| 4550fff1af | |||
| 7c4e5b14cb | |||
| f72d3dfa32 | |||
| e67539752e | |||
| db0f36f431 | |||
| 7e9d3f2b50 | |||
| 4015e0666e | |||
| cee62eb28e | |||
| 735080a84b | |||
| 161e7997fa | |||
| 06dbd774a1 | |||
| a280501ff1 | |||
| a56d9a6661 | |||
| b0048c0aca | |||
| 0983ed29bb | |||
| f67a7b64c8 | |||
| 628e84068e | |||
| 78a32e93fd | |||
| 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 | |||
| 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 | |||
| 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 | |||
| 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 |
@@ -0,0 +1 @@
|
||||
*.py diff=python
|
||||
@@ -1,6 +1,14 @@
|
||||
mnexec
|
||||
*.pyc
|
||||
*~
|
||||
*.1
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
\#*\#
|
||||
mininet.egg-info
|
||||
build
|
||||
dist
|
||||
doc/html
|
||||
doc/latex
|
||||
trunk
|
||||
|
||||
|
||||
@@ -25,9 +25,6 @@ ignore=CVS
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Set the cache size for astng objects.
|
||||
cache-size=500
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
@@ -35,24 +32,15 @@ load-plugins=
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Enable only checker(s) with the given id(s). This option conflicts with the
|
||||
# disable-checker option
|
||||
#enable-checker=
|
||||
# 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=
|
||||
|
||||
# Enable all checker(s) except those with the given id(s). This option
|
||||
# conflicts with the enable-checker option
|
||||
#disable-checker=
|
||||
|
||||
# Enable all messages in the listed categories (IRCWEF).
|
||||
#enable-msg-cat=
|
||||
|
||||
# Disable all messages in the listed categories (IRCWEF).
|
||||
disable-msg-cat=IR
|
||||
|
||||
# Enable the message(s) with the given id(s).
|
||||
#enable-msg=
|
||||
|
||||
# Disable the message(s) with the given id(s).
|
||||
# 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=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
|
||||
|
||||
|
||||
@@ -60,7 +48,7 @@ disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html
|
||||
output-format=text
|
||||
output-format=colorized
|
||||
|
||||
# Include message's id in outpu
|
||||
include-ids=yes
|
||||
@@ -276,7 +264,7 @@ int-import-graph=
|
||||
max-line-length=80
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=1000
|
||||
max-module-lines=1500
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
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
|
||||
|
||||
Bob Lantz
|
||||
Brandon Heller
|
||||
Nikhil Handigol
|
||||
Vimal Jeyakumar
|
||||
Brian O'Connor
|
||||
|
||||
Additional Mininet Contributors
|
||||
|
||||
Gustavo Pantuza Coelho Pinto
|
||||
Ryan Cox
|
||||
Shaun Crampton
|
||||
David Erickson
|
||||
Glen Gibb
|
||||
Andrew Ferguson
|
||||
Eder Leao Fernandes
|
||||
Vitaly Ivanov
|
||||
Rich Lane
|
||||
Murphy McCauley
|
||||
José Pedro Oliveira
|
||||
James Page
|
||||
Angad Singh
|
||||
Piyush Srivastava
|
||||
Ed Swierk
|
||||
Isaku Yamahata
|
||||
|
||||
Thanks also to everyone who has submitted issues and pull
|
||||
requests on github, and to our friendly mininet-discuss
|
||||
mailing list!
|
||||
@@ -1,273 +1,173 @@
|
||||
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 1.0.0
|
||||
|
||||
Mininet 2.1.0+
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using
|
||||
a pre-built VM image, and 2) native installation on Ubuntu or Debian.
|
||||
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 "install" - use our pre-built VM image!
|
||||
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://openflow.org/mininet
|
||||
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 wiki page.
|
||||
Boot up the VM image, log in, and follow the instructions on the
|
||||
Mininet web site.
|
||||
|
||||
An additional advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
One advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
|
||||
2. Native installation (experimental!) for Ubuntu 10.04 LTS
|
||||
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.
|
||||
|
||||
If you are running Ubuntu 10.04 LTS (or possibly Debian 5), you may be
|
||||
able to use our handy install.sh script, which is in mininet/util.
|
||||
2. Next-easiest option: use our Ubuntu package!
|
||||
|
||||
WARNING: USE AT YOUR OWN RISK!
|
||||
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
|
||||
12.10+:
|
||||
|
||||
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, openvswitch and noxcore. 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!
|
||||
sudo apt-get install mininet
|
||||
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
you may use
|
||||
Note: if you are upgrading from an older version of Mininet, make
|
||||
sure you remove the old OVS from `/usr/local`:
|
||||
|
||||
$ mininet/util/install.sh
|
||||
sudo rm /usr/local/bin/ovs*
|
||||
sudo rm /usr/local/sbin/ovs*
|
||||
|
||||
This takes about 20-30 minutes.
|
||||
3. Native installation from source
|
||||
|
||||
Alternately, you can install just the pieces you need.
|
||||
3.1. Native installation from source on Ubuntu 12.04+
|
||||
|
||||
We recommend the following steps, in order:
|
||||
If you're reading this, you've probably already done so, but the
|
||||
command to download the Mininet source code is:
|
||||
|
||||
[a) On Debian 5, first install a Mininet-compatible kernel:
|
||||
$ mininet/util/install.sh -k
|
||||
Reboot and run 'uname -r' to make sure you're running the new kernel.]
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
|
||||
b) Install mininet and its dependencies:
|
||||
$ mininet/util/install.sh -n
|
||||
|
||||
c) Install OpenFlow 1.0 and associated useful software
|
||||
$ mininet/util/install.sh -f
|
||||
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, use:
|
||||
|
||||
d) Install Open vSwitch and its kernel module
|
||||
$ mininet/util/install.sh -vm
|
||||
git clone git://github.com/mininet/mininet
|
||||
git checkout -b 2.1.0 2.1.0
|
||||
|
||||
e) If you wish to install the version of NOX we use in the tutorial:
|
||||
$ mininet/util/install.sh -x
|
||||
If you are running Ubuntu, you may be able to use our handy
|
||||
`install.sh` script, which is in `mininet/util`.
|
||||
|
||||
Note: NOX development is progressing over time, so after you complete
|
||||
the tutorial you may wish to install the latest and greatest NOX from
|
||||
noxrepo.org.
|
||||
*WARNING: USE AT YOUR OWN RISK!*
|
||||
|
||||
Good luck! Some additional installation notes are provided below, for
|
||||
the brave and/or Linux-savvy, or those who are trying to understand what
|
||||
is installed and why.
|
||||
`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..
|
||||
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!
|
||||
|
||||
p.s. Note that only one instance of Mininet is currently supported on a single
|
||||
machine - that's one reason we recommend using a VM to run it.
|
||||
To install Mininet itself, the OpenFlow reference implementation, and
|
||||
Open vSwitch, you may use:
|
||||
|
||||
---
|
||||
mininet/util/install.sh -fnv
|
||||
|
||||
Mininet Manual Installation Notes
|
||||
This should be reasonably quick, and the following command should
|
||||
work after the installation:
|
||||
|
||||
These installation notes assume you understand how to do things like
|
||||
compile kernels, apply patches, configure networks, write code, etc.. If
|
||||
this is unfamiliar territory, or if you run into trouble, we recommend
|
||||
using one of our pre-built virtual machine images (see above.)
|
||||
sudo mn --test pingall
|
||||
|
||||
If you wish to try to create a VM to run Mininet, you may also wish
|
||||
to look at the Wiki page:
|
||||
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:
|
||||
|
||||
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
|
||||
mininet/util/install.sh -a
|
||||
|
||||
0. Obtaining Mininet
|
||||
This takes about 4 minutes on our test system.
|
||||
|
||||
If you're reading this, you've already done it, but the command to
|
||||
download mininet is:
|
||||
|
||||
git clone git://openflow.org/mininet.git
|
||||
|
||||
1. Core Mininet installation
|
||||
You can change the directory where the dependencies are installed using
|
||||
the -s <directory> flag.
|
||||
|
||||
The core Mininet installation requires gcc, make, python,
|
||||
and setuptools. On Ubuntu and Debian you may install them with:
|
||||
|
||||
# aptitude install gcc make python setuptools
|
||||
mininet/util/install.sh -s <directory> -a
|
||||
|
||||
To install Mininet itself, with root privileges:
|
||||
|
||||
# cd mininet
|
||||
# make install
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
|
||||
This places the mininet package in /usr/lib/python-*/site-packages/,
|
||||
so that 'import mininet' will work, and installs the primary mn
|
||||
script (mn) as well as its helper utility (mnexec.)
|
||||
|
||||
On Ubuntu and Debian, Mininet's dependencies and core files may also be
|
||||
installed using mininet/util/install.sh -n
|
||||
As root execute the following operations:
|
||||
|
||||
2. Installation script for Ubuntu/Debian Lenny
|
||||
* install git
|
||||
|
||||
If you are running Ubuntu 10.04 or Debian Lenny, you may be able to use the
|
||||
util/install.sh script to install a compatible Linux kernel as well as
|
||||
other software including the OpenFlow reference implementation, the Open
|
||||
vSwitch switch implementation, and the NOX OpenFlow controller.
|
||||
yum install git
|
||||
|
||||
Many different installation options are possible by passing different
|
||||
options to install.sh; install.sh -h lists them all.
|
||||
|
||||
Assuming the mininet source tree is installed in ~/mininet, the steps to run
|
||||
install.sh to install EVERYTHING we use for OpenFlow tutorials are:
|
||||
|
||||
% cd
|
||||
% time ~/mininet/util/install.sh # installs tons of stuff
|
||||
% sudo reboot # to load new kernel
|
||||
% ~/mininet/util/install.sh -c # to clean out unneeded kernel stuff
|
||||
* create an user account (e.g. mininet) and add it to the wheel group
|
||||
|
||||
This installs a lot of useful software, but it will take a while (30
|
||||
minutes or more, depending on your network connection, computer, etc..)
|
||||
|
||||
Probably the minimal semi-useful configuration would be to install
|
||||
Mininet itself, kernel support if necessary, and either the
|
||||
reference OpenFlow switch or Open vSwitch. This could be installed
|
||||
as follows:
|
||||
|
||||
% sudo ~/mininet/util/install.sh -knvm
|
||||
|
||||
Respectively, this installs kernel support, core mininet dependencies,
|
||||
Open vSwitch, and the Open vSwitch kernel module. If a new kernel was
|
||||
installed, then a reboot may be required.
|
||||
useradd [...] mininet
|
||||
usermod -a -G wheel mininet
|
||||
|
||||
If install.sh cannot be used for some reason (e.g. you're on Fedora
|
||||
or some other Linux - please don't say CentOS) or if you don't want to
|
||||
install all of these components (they're useful!), the kernel and
|
||||
OpenFlow software requirements are described in steps [3] and [4],
|
||||
which follow.
|
||||
* change the SElinux setting to permissive. It can be done
|
||||
temporarily with:
|
||||
|
||||
If you successfully used install.sh, congratulations! You're basically
|
||||
done. Proceed to step [6] for additional advice.
|
||||
|
||||
3. Linux Kernel requirements
|
||||
setenforce 0
|
||||
|
||||
Mininet requires a kernel built with network namespace support enabled,
|
||||
i.e. with CONFIG_NET_NS=Y, such as the kernel shipped with
|
||||
Ubuntu 10.04 LTS, currently 2.6.32. On Ubuntu 10.04, you should not need
|
||||
to install or build a custom kernel, although 2.6.33+ is faster at
|
||||
tearing down virtual ethernet pairs.
|
||||
then login with the new account (e.g. mininet) and do the following:
|
||||
|
||||
For Ubuntu and Debian, we provide a 2.6.33 kernel package which you may be
|
||||
able to install using "util/install.sh -k". Note our kernel package
|
||||
requires an ext2 or ext3 root file system, so it won't work if you have
|
||||
a default Ubuntu install, which uses ext4.
|
||||
|
||||
If your kernel wasn't compiled with CONFIG_NET_NS=Y, you will need to
|
||||
build and install a kernel that does! >= 2.6.33 works better, but may
|
||||
be harder to get working, depending on your Linux distribution.
|
||||
|
||||
A script for building Debian packages for 2.6.33.1 is provided in
|
||||
mininet/util/kbuild. You may wish to read it, as it applies patches
|
||||
to enable 2.6.33.1 to build under debian-stable, and to enable the
|
||||
tun driver to work correctly with Mininet.
|
||||
|
||||
Earlier kernels (e.g. 2.6.29) work with CONFIG_NET_NS enabled and no
|
||||
additional patches, but are much slower at removing veth interfaces,
|
||||
resulting in much slower switch shutdown.
|
||||
* clone the Mininet repository
|
||||
|
||||
For scalable configurations, you might need to increase some of your
|
||||
kernel limits. Sample params are in util/sysctl_addon, which can be
|
||||
appended to /etc/sysctl.conf (and modified as necessary for your
|
||||
desired configuration):
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
|
||||
sudo su -c "cat sysctl_addon >> /etc/sysctl.conf"
|
||||
* install Mininet, the OpenFlow reference implementation, and
|
||||
Open vSwitch
|
||||
|
||||
To save the config change, run:
|
||||
mininet/util/install.sh -fnv
|
||||
|
||||
sudo sysctl -p
|
||||
|
||||
4. OpenFlow software and configuration requirements
|
||||
* enable and start openvswitch
|
||||
|
||||
Mininet requires either the reference OpenFlow switch implementation
|
||||
(from openflowswitch.org) or Open vSwitch (openvswitch.org) to be
|
||||
installed. "make test" requires the reference user space
|
||||
implementations as well as Open vSwitch. Note the reference kernel
|
||||
implementation is not currently included in OpenFlow 1.0.
|
||||
|
||||
On Ubuntu and Debian, the install.sh script may be used with the '-f'
|
||||
option to install the OpenFlow reference implementation, the '-v' option
|
||||
to build Open vSwitch, and the '-m' option to install the Open vSwitch
|
||||
kernel module into /lib/modules (note: you must build Open vSwitch first!)
|
||||
|
||||
Mininet will automatically load and remove kernel module dependencies
|
||||
for supported switch types, using modprobe and rmmod - but these
|
||||
modules must be in a location where modprobe can find them (e.g.
|
||||
something like /lib/modules/`uname -r`/kernel/drivers/net/)
|
||||
sudo systemctl enable openvswitch
|
||||
sudo systemctl start openvswitch
|
||||
|
||||
The reference OpenFlow controller (controller(8)) only supports 16
|
||||
switches by default! If you wish to run a network with more than 16
|
||||
switches, please recompile controller(8) with larger limits, or use a
|
||||
different controller such as nox. A patch to controller(8) is included
|
||||
as util/openflow-patches/controller.patch.
|
||||
|
||||
5. Other software dependencies
|
||||
* test the mininet installation
|
||||
|
||||
On Ubuntu and Debian, other Mininet dependencies may be installed using
|
||||
the '-n' option of the install.sh script.
|
||||
sudo mn --test pingall
|
||||
|
||||
To run the iperf test, you need to install iperf:
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||
|
||||
sudo aptitude/yum install iperf
|
||||
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:
|
||||
|
||||
We assume you already have ping installed. ;-)
|
||||
|
||||
To use xterm or sshd with Mininet, you need the following:
|
||||
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
|
||||
time install-mininet-vm.sh
|
||||
|
||||
sudo aptitude/yum install sshd xterm screen
|
||||
|
||||
Some examples may have additional requirements - consult the specific
|
||||
example file for details.
|
||||
|
||||
The install.sh script has an '-x' option to install the version of
|
||||
NOX from the OpenFlow tutorial.
|
||||
|
||||
6. Other notes and recommendations
|
||||
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 OpenFlow implementation (either the reference user or kernel
|
||||
space implementations, or Open vSwitch.) Appropriate kernel
|
||||
modules (e.g. tun and ofdatapath for the reference kernel
|
||||
implementation) must be loaded.
|
||||
|
||||
* 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.
|
||||
|
||||
If you did not install certain useful packages and you wish to later,
|
||||
it may be possible to install them using install.sh.
|
||||
|
||||
Mininet should be run either on a machine with
|
||||
no other important processes, or on a virtual machine (recommended!)
|
||||
|
||||
Multiple concurrent Mininet instances are not supported!
|
||||
|
||||
Good luck!
|
||||
|
||||
Mininet Team
|
||||
|
||||
---
|
||||
|
||||
Historical information on OpenFlow 0.8.9 and the reference kernel module:
|
||||
|
||||
The kernel reference implementation has been deprecated, but it may
|
||||
be possible to get it work with Mininet.
|
||||
|
||||
To switch to the most recent OpenFlow 0.8.9 release branch (the most
|
||||
recent one with full NOX support and kernel datapath support) in your
|
||||
OpenFlow git tree:
|
||||
|
||||
git checkout -b release/0.8.9 remotes/origin/release/0.8.9
|
||||
|
||||
A patch to enable datapath.c to compile with recent kernels
|
||||
is included in util/openflow-patches/datapath.patch.
|
||||
|
||||
In OpenFlow 1.0, switch port numbering starts at 1 (for better or for worse.)
|
||||
To run with previous versions of OpenFlow, it may be necessary
|
||||
to change SWITCH_PORT_BASE from 1 to 0 in node.py.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
Mininet 1.0.0 License
|
||||
Mininet 2.1.0+ License
|
||||
|
||||
Copyright (c) 2009-2011 Bob Lantz and Brandon Heller
|
||||
Copyright (c) 2013 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
|
||||
|
||||
@@ -1,30 +1,67 @@
|
||||
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
|
||||
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 bin/mnexec
|
||||
|
||||
MININET = mininet/*.py
|
||||
TEST = mininet/test/*.py
|
||||
EXAMPLES = examples/*.py
|
||||
BIN = bin/mn
|
||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||
|
||||
P8IGN = E251,E201,E302,E202
|
||||
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)
|
||||
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
|
||||
|
||||
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
|
||||
|
||||
install: mnexec
|
||||
cp mnexec bin/
|
||||
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
|
||||
|
||||
doc:
|
||||
doxygen doxygen.cfg
|
||||
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,86 +0,0 @@
|
||||
|
||||
Mininet: A Simple Virtual Testbed for OpenFlow/SDN
|
||||
or
|
||||
How to Squeeze a 1024-node OpenFlow Network onto your Laptop
|
||||
|
||||
Mininet 1.0.0
|
||||
|
||||
---
|
||||
Welcome to Mininet!
|
||||
|
||||
Mininet creates OpenFlow test networks by using process-based
|
||||
virtualization and network namespaces.
|
||||
|
||||
Simulated hosts (as well as switches and controllers with the user
|
||||
datapath) are created as processes in separate network namespaces. This
|
||||
allows a complete OpenFlow network to be simulated on top of a single
|
||||
Linux kernel.
|
||||
|
||||
Mininet may be invoked directly from the command line, and also provides a
|
||||
handy Python API for creating networks of varying sizes and topologies.
|
||||
|
||||
Mininet is currently in *limited alpha release*. We encourage you to
|
||||
experiment with it and hope that you will provide us with feedback on
|
||||
features, documentation, and how you're using it. We plan to make it
|
||||
available publicly via a GPL or BSD license (probably in April), but please
|
||||
don't distribute the code or URLs yet! The feedback you provide will help
|
||||
us improve Mininet for general release.
|
||||
|
||||
In order to run Mininet, you must have:
|
||||
|
||||
* A Linux 2.6.26 or greater kernel compiled with network namespace support
|
||||
enabled (see INSTALL for additional information.)
|
||||
|
||||
* An OpenFlow implementation (either the reference user or kernel
|
||||
space implementations, or Open vSwitch.) Appropriate kernel modules
|
||||
(e.g. tun and ofdatapath for the reference kernel implementation) must
|
||||
be loaded.
|
||||
|
||||
* Python, bash, ping, iperf, etc.
|
||||
|
||||
* Root privileges (required for network device access)
|
||||
|
||||
Currently Mininet includes:
|
||||
|
||||
- A simple node infrastructure (Host, Switch, Controller classes) for
|
||||
creating virtual OpenFlow networks
|
||||
|
||||
- A simple network infrastructure (Mininet class) supporting parametrized
|
||||
topologies (Topo subclasses.) For example, a tree network may be created
|
||||
with the command
|
||||
|
||||
# mn --topo tree,depth=2,fanout=3
|
||||
|
||||
- Basic tests, including connectivity (ping) and bandwidth (iperf)
|
||||
|
||||
- A command-line interface (CLI class) which provides useful
|
||||
diagnostic commands, as well as the ability to send 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
|
||||
|
||||
- Examples (in the examples/ directory) to help you get started.
|
||||
|
||||
Batteries are not included (yet!)
|
||||
|
||||
However, some preliminary installation notes are included in the INSTALL
|
||||
file.
|
||||
|
||||
Additionally, much useful information, including a Mininet tutorial,
|
||||
is available on the Mininet wiki:
|
||||
|
||||
http://openflow.org/mininet
|
||||
|
||||
Enjoy, and good luck!
|
||||
|
||||
---
|
||||
Bob Lantz
|
||||
rlantz@cs.stanford.edu
|
||||
@@ -0,0 +1,133 @@
|
||||
Mininet: Rapid Prototyping for Software Defined Networks
|
||||
========================================================
|
||||
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
Version 2.1.0+
|
||||
|
||||
### What is Mininet?
|
||||
|
||||
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:
|
||||
|
||||
`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 2.1.0+
|
||||
|
||||
Mininet 2.1.0+ provides a number of bug fixes as well as
|
||||
several new features, including:
|
||||
|
||||
* Convenient access to `Mininet()` as a dict of nodes
|
||||
* X11 tunneling (wireshark in Mininet hosts, finally!)
|
||||
* Accurate reflection of the `Mininet()` object in the CLI
|
||||
* Automatically detecting and adjusting resource limits
|
||||
* Automatic cleanup on failure of the `mn` command
|
||||
* Preliminary support for running OVS in user space mode
|
||||
* Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch
|
||||
* support for installing the OpenFlow 1.3 versions of the reference
|
||||
user switch and NOX from CPqD
|
||||
* The ability to import modules from `mininet.examples`
|
||||
|
||||
We have provided several new examples (which can easily be
|
||||
imported to provide useful functionality) including:
|
||||
|
||||
* Modeling separate control and data networks: `mininet.examples.controlnet`
|
||||
* Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat`
|
||||
* Creating per-host custom directories using bind mounts: `mininet.examples.bind`
|
||||
|
||||
Note that examples contain experimental features which might
|
||||
"graduate" into mainline Mininet in the future, but they should
|
||||
not be considered a stable part of the Mininet API!
|
||||
|
||||
### Installation
|
||||
|
||||
See `INSTALL` for installation instructions and details.
|
||||
|
||||
### Documentation
|
||||
|
||||
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.)
|
||||
|
||||
### 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>
|
||||
|
||||
### Contributing
|
||||
|
||||
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 to the project
|
||||
(see CONTRIBUTORS for more info!)
|
||||
|
||||
Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
|
||||
### Credits
|
||||
|
||||
The Mininet 2.1.0+ Team:
|
||||
|
||||
* Bob Lantz
|
||||
* Brian O'Connor
|
||||
@@ -12,69 +12,74 @@ Example to pull custom params (topo, switch, etc.) from a file:
|
||||
"""
|
||||
|
||||
from optparse import OptionParser
|
||||
import os.path
|
||||
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
|
||||
from mininet.net import Mininet, init
|
||||
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
|
||||
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
|
||||
from mininet.log import lg, LEVELS, info, debug, error
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
NOX, RemoteController, DefaultController,
|
||||
UserSwitch, OVSSwitch,
|
||||
OVSLegacyKernelSwitch, IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import makeNumeric
|
||||
from mininet.topolib import TreeTopo, TorusTopo
|
||||
from mininet.util import custom, customConstructor
|
||||
from mininet.util import buildTopo
|
||||
|
||||
|
||||
# built in topologies, created only when run
|
||||
TOPODEF = 'minimal'
|
||||
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo }
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo,
|
||||
'torus': TorusTopo }
|
||||
|
||||
SWITCHDEF = 'ovsk'
|
||||
SWITCHES = { 'kernel': KernelSwitch,
|
||||
'user': UserSwitch,
|
||||
'ovsk': OVSKernelSwitch }
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
'ovs': OVSSwitch,
|
||||
# Keep ovsk for compatibility with 2.0
|
||||
'ovsk': OVSSwitch,
|
||||
'ovsl': OVSLegacyKernelSwitch,
|
||||
'ivs': IVSSwitch,
|
||||
'lxbr': LinuxBridge }
|
||||
|
||||
HOSTDEF = 'process'
|
||||
HOSTS = { 'process': Host }
|
||||
HOSTDEF = 'proc'
|
||||
HOSTS = { 'proc': Host,
|
||||
'rt': custom( CPULimitedHost, sched='rt' ),
|
||||
'cfs': custom( CPULimitedHost, sched='cfs' ) }
|
||||
|
||||
CONTROLLERDEF = 'ref'
|
||||
# a and b are the name and inNamespace params.
|
||||
CONTROLLERDEF = 'default'
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'nox_dump': lambda name: NOX( name, 'packetdump' ),
|
||||
'nox_pysw': lambda name: NOX( name, 'pyswitch' ),
|
||||
'remote': lambda name: None,
|
||||
'none': lambda name: None }
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'default': DefaultController,
|
||||
'none': lambda name: None }
|
||||
|
||||
LINKDEF = 'default'
|
||||
LINKS = { 'default': Link,
|
||||
'tc': TCLink }
|
||||
|
||||
|
||||
# optional tests to run
|
||||
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||
'none' ]
|
||||
'none' ]
|
||||
|
||||
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
|
||||
|
||||
def buildTopo( topo ):
|
||||
"Create topology from string with format (object, arg1, arg2,...)."
|
||||
topo_split = topo.split( ',' )
|
||||
topo_name = topo_split[ 0 ]
|
||||
topo_params = topo_split[ 1: ]
|
||||
|
||||
# Convert int and float args; removes the need for every topology to
|
||||
# be flexible with input arg formats.
|
||||
topo_seq_params = [ s for s in topo_params if '=' not in s ]
|
||||
topo_seq_params = [ makeNumeric( s ) for s in topo_seq_params ]
|
||||
topo_kw_params = {}
|
||||
for s in [ p for p in topo_params if '=' in p ]:
|
||||
key, val = s.split( '=' )
|
||||
topo_kw_params[ key ] = makeNumeric( val )
|
||||
|
||||
if topo_name not in TOPOS.keys():
|
||||
raise Exception( 'Invalid topo_name %s' % topo_name )
|
||||
return TOPOS[ topo_name ]( *topo_seq_params, **topo_kw_params )
|
||||
ALTSPELLING = { 'pingall': 'pingAll',
|
||||
'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp',
|
||||
'iperfUDP': 'iperfUdp' }
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
@@ -86,16 +91,21 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
help: string"""
|
||||
if default not in choicesDict:
|
||||
raise Exception( 'Invalid default %s for choices dict: %s' %
|
||||
( default, name ) )
|
||||
( default, name ) )
|
||||
if not helpStr:
|
||||
helpStr = '[' + ' '.join( choicesDict.keys() ) + ']'
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
opts.add_option( '--' + name,
|
||||
type='choice',
|
||||
choices=choicesDict.keys(),
|
||||
default = default,
|
||||
help = helpStr )
|
||||
type='string',
|
||||
default = default,
|
||||
help = helpStr )
|
||||
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
print "%s" % VERSION
|
||||
exit()
|
||||
|
||||
class MininetRunner( object ):
|
||||
"Build, setup, and run Mininet."
|
||||
|
||||
@@ -124,11 +134,11 @@ class MininetRunner( object ):
|
||||
|
||||
def parseCustomFile( self, fileName ):
|
||||
"Parse custom file and add params before parsing cmd-line options."
|
||||
custom = {}
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
execfile( fileName, custom, custom )
|
||||
for name in custom:
|
||||
self.setCustom( name, custom[ name ] )
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
@@ -136,61 +146,72 @@ class MininetRunner( object ):
|
||||
"""Parse command-line args and return options object.
|
||||
returns: opts parse options dict"""
|
||||
if '--custom' in sys.argv:
|
||||
print "custom in sys.argv"
|
||||
index = sys.argv.index( '--custom' )
|
||||
if len( sys.argv ) > index + 1:
|
||||
custom = sys.argv[ index + 1 ]
|
||||
self.parseCustomFile( custom )
|
||||
filename = sys.argv[ index + 1 ]
|
||||
self.parseCustomFile( filename )
|
||||
else:
|
||||
raise Exception( 'Custom file name not found' )
|
||||
|
||||
opts = OptionParser()
|
||||
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, CONTROLLERDEF, 'controller' )
|
||||
addDictOption( opts, LINKS, LINKDEF, 'link' )
|
||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||
|
||||
opts.add_option( '--topo', type='string', default=TOPODEF,
|
||||
help='[' + ' '.join( TOPOS.keys() ) + '],arg1,arg2,'
|
||||
'...argN')
|
||||
opts.add_option( '--clean', '-c', action='store_true',
|
||||
default=False, help='clean and exit' )
|
||||
default=False, help='clean and exit' )
|
||||
opts.add_option( '--custom', type='string', default=None,
|
||||
help='read custom topo and node params from .py file' )
|
||||
help='read custom topo and node params from .py' +
|
||||
'file' )
|
||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||
default=TESTS[ 0 ],
|
||||
help='[' + ' '.join( TESTS ) + ']' )
|
||||
default=TESTS[ 0 ],
|
||||
help='|'.join( TESTS ) )
|
||||
opts.add_option( '--xterms', '-x', action='store_true',
|
||||
default=False, help='spawn xterms for each node' )
|
||||
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='set MACs equal to DPIDs' )
|
||||
default=False, help='automatically set host MACs' )
|
||||
opts.add_option( '--arp', action='store_true',
|
||||
default=False, help='set all-pairs ARP entries' )
|
||||
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( '--ip', type='string', default='127.0.0.1',
|
||||
help='[ip address as a dotted decimal string for a'
|
||||
'remote controller]' )
|
||||
opts.add_option( '--port', type='int', default=6633,
|
||||
help='[port integer for a listening remote'
|
||||
' controller]' )
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6634,
|
||||
help='[base port for passive switch listening'
|
||||
' controller]' )
|
||||
help='base port for passive switch listening' )
|
||||
opts.add_option( '--nolistenport', action='store_true',
|
||||
default=False, help="don't use passive listening port")
|
||||
default=False, help="don't use passive listening " +
|
||||
"port")
|
||||
opts.add_option( '--pre', type='string', default=None,
|
||||
help='[CLI script to run before tests]' )
|
||||
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( '--prefixlen', type='int', default=8,
|
||||
help='[prefix length (e.g. /8) for automatic '
|
||||
'network configuration]' )
|
||||
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='store_true',
|
||||
default=False, help="adds a NAT to the topology "
|
||||
"that connects Mininet to the physical network" )
|
||||
opts.add_option( '--version', action='callback', callback=version )
|
||||
|
||||
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."
|
||||
|
||||
@@ -202,9 +223,6 @@ class MininetRunner( object ):
|
||||
% self.options.verbosity )
|
||||
lg.setLogLevel( self.options.verbosity )
|
||||
|
||||
# validate environment setup
|
||||
init()
|
||||
|
||||
def begin( self ):
|
||||
"Create and run mininet."
|
||||
|
||||
@@ -214,35 +232,37 @@ class MininetRunner( object ):
|
||||
|
||||
start = time.time()
|
||||
|
||||
topo = buildTopo( self.options.topo )
|
||||
switch = SWITCHES[ self.options.switch ]
|
||||
host = HOSTS[ self.options.host ]
|
||||
controller = CONTROLLERS[ self.options.controller ]
|
||||
if self.options.controller == 'remote':
|
||||
controller = lambda a: RemoteController( a,
|
||||
defaultIP=self.options.ip,
|
||||
port=self.options.port )
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customConstructor( SWITCHES, self.options.switch )
|
||||
host = customConstructor( HOSTS, self.options.host )
|
||||
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||
link = customConstructor( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( self.options )
|
||||
|
||||
# We should clarify what this is actually for...
|
||||
# It seems like it should be default values for the
|
||||
# *data* network, so it may be misnamed.
|
||||
controllerParams = ControllerParams( '10.0.0.0',
|
||||
self.options.prefixlen)
|
||||
|
||||
inNamespace = self.options.innamespace
|
||||
Net = MininetWithControlNet if inNamespace else Mininet
|
||||
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
|
||||
mn = Mininet( topo, switch, host, controller, controllerParams,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, listenPort=listenPort )
|
||||
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.nat:
|
||||
nat = mn.addNAT()
|
||||
nat.configDefault()
|
||||
|
||||
if self.options.pre:
|
||||
CLI( mn, script=self.options.pre )
|
||||
@@ -255,12 +275,14 @@ class MininetRunner( object ):
|
||||
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:
|
||||
@@ -273,4 +295,21 @@ class MininetRunner( object ):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
MininetRunner()
|
||||
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()
|
||||
|
||||
+13
-24
@@ -1,7 +1,5 @@
|
||||
"""Custom topology example
|
||||
|
||||
author: Brandon Heller (brandonh@stanford.edu)
|
||||
|
||||
Two directly connected switches plus a host for each switch:
|
||||
|
||||
host --- switch --- switch --- host
|
||||
@@ -10,36 +8,27 @@ 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, Node
|
||||
from mininet.topo import Topo
|
||||
|
||||
class MyTopo( Topo ):
|
||||
"Simple topology example."
|
||||
|
||||
def __init__( self, enable_all = True ):
|
||||
def __init__( self ):
|
||||
"Create custom topo."
|
||||
|
||||
# Add default members to class.
|
||||
super( MyTopo, self ).__init__()
|
||||
# Initialize topology
|
||||
Topo.__init__( self )
|
||||
|
||||
# Set Node IDs for hosts and switches
|
||||
leftHost = 1
|
||||
leftSwitch = 2
|
||||
rightSwitch = 3
|
||||
rightHost = 4
|
||||
# Add hosts and switches
|
||||
leftHost = self.addHost( 'h1' )
|
||||
rightHost = self.addHost( 'h2' )
|
||||
leftSwitch = self.addSwitch( 's3' )
|
||||
rightSwitch = self.addSwitch( 's4' )
|
||||
|
||||
# Add nodes
|
||||
self.add_node( leftSwitch, Node( is_switch=True ) )
|
||||
self.add_node( rightSwitch, Node( is_switch=True ) )
|
||||
self.add_node( leftHost, Node( is_switch=False ) )
|
||||
self.add_node( rightHost, Node( is_switch=False ) )
|
||||
|
||||
# Add edges
|
||||
self.add_edge( leftHost, leftSwitch )
|
||||
self.add_edge( leftSwitch, rightSwitch )
|
||||
self.add_edge( rightSwitch, rightHost )
|
||||
|
||||
# Consider all switches and hosts 'on'
|
||||
self.enable_all()
|
||||
# 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)
|
||||
@@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
PROJECT_NAME = Mininet
|
||||
PROJECT_NAME = "Mininet Python API Reference Manual"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
||||
# This could be handy for archiving the generated documentation or
|
||||
@@ -114,7 +114,7 @@ FULL_PATH_NAMES = YES
|
||||
# If left blank the directory from which doxygen is run is used as the
|
||||
# path to strip.
|
||||
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_PATH =
|
||||
|
||||
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
|
||||
# the path mentioned in the documentation of a class, which tells
|
||||
@@ -919,7 +919,7 @@ COMPACT_LATEX = NO
|
||||
# by the printer. Possible values are: a4, a4wide, letter, legal and
|
||||
# executive. If left blank a4wide will be used.
|
||||
|
||||
PAPER_TYPE = a4wide
|
||||
PAPER_TYPE = letter
|
||||
|
||||
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
|
||||
# packages that should be included in the LaTeX output.
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
Mininet Examples
|
||||
|
||||
These examples are intended to help you get started using
|
||||
Mininet's Python API.
|
||||
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
emptynet.py:
|
||||
|
||||
This example demonstrates creating an empty network (i.e. with no
|
||||
topology object) and adding nodes to it.
|
||||
|
||||
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.
|
||||
|
||||
miniedit.py:
|
||||
|
||||
This example demonstrates creating a network via a graphical editor.
|
||||
|
||||
multitest.py:
|
||||
|
||||
This example creates a network and runs multiple tests on it.
|
||||
|
||||
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.
|
||||
|
||||
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.)
|
||||
|
||||
treeping64:
|
||||
|
||||
This example creates a 64-host tree network, and attempts to check full
|
||||
connectivity using ping, for three different switch/datapath types.
|
||||
|
||||
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.)
|
||||
|
||||
udpbwtest.py:
|
||||
|
||||
This example shows how to run a test across an entire network, and monitor
|
||||
the output of a set of hosts in real time.
|
||||
@@ -0,0 +1,129 @@
|
||||
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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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`?"
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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
|
||||
@@ -0,0 +1,4 @@
|
||||
"""
|
||||
Mininet Examples
|
||||
See README for details
|
||||
"""
|
||||
+14
-3
@@ -2,18 +2,25 @@
|
||||
|
||||
"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
|
||||
|
||||
ensureRoot()
|
||||
|
||||
print "*** Creating nodes"
|
||||
h1 = Host( 'h1' )
|
||||
|
||||
root = Host( 'root', inNamespace=False )
|
||||
|
||||
print "*** Creating links"
|
||||
h1.linkTo( root )
|
||||
|
||||
print h1
|
||||
|
||||
print "*** Configuring nodes"
|
||||
h1.setIP( h1.intfs[ 0 ], '10.0.0.1', 8 )
|
||||
root.setIP( root.intfs[ 0 ], '10.0.0.2', 8 )
|
||||
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' )
|
||||
@@ -21,6 +28,10 @@ f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
|
||||
f.close()
|
||||
|
||||
print "*** Running sshd"
|
||||
h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
|
||||
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 )
|
||||
|
||||
print "*** You may now ssh into", h1.name, "at", h1.IP()
|
||||
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/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, HostWithPrivateDirs
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info, debug
|
||||
|
||||
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( HostWithPrivateDirs,
|
||||
privateDirs=privateDirs )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
net.start()
|
||||
directories = []
|
||||
for directory in privateDirs:
|
||||
directories.append( directory[ 0 ]
|
||||
if isinstance( directory, tuple )
|
||||
else directory )
|
||||
info( 'Private Directories:', directories, '\n' )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testHostWithPrivateDirs()
|
||||
info( 'Done.\n')
|
||||
|
||||
|
||||
+28
-27
@@ -74,11 +74,11 @@ class Console( Frame ):
|
||||
"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 )
|
||||
**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 )
|
||||
command=text.yview )
|
||||
text.configure( yscrollcommand=ybar.set )
|
||||
text.pack( side='left', expand=True, fill='both' )
|
||||
ybar.pack( side='right', fill='y' )
|
||||
@@ -95,7 +95,7 @@ class Console( Frame ):
|
||||
# way to trigger a file event handler from Tk's
|
||||
# event loop!
|
||||
self.tk.createfilehandler( self.node.stdout, READABLE,
|
||||
self.handleReadable )
|
||||
self.handleReadable )
|
||||
|
||||
# We're not a terminal (yet?), so we ignore the following
|
||||
# control characters other than [\b\n\r]
|
||||
@@ -107,8 +107,10 @@ class Console( Frame ):
|
||||
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:
|
||||
self.outputHook( self, text )
|
||||
outputHook = self.outputHook
|
||||
outputHook( self, text )
|
||||
|
||||
def handleKey( self, event ):
|
||||
"If it's an interactive command, send it to the node."
|
||||
@@ -130,27 +132,22 @@ class Console( Frame ):
|
||||
self.sendCmd( cmd )
|
||||
|
||||
# Callback ignores event
|
||||
# pylint: disable-msg=W0613
|
||||
def handleInt( self, event=None ):
|
||||
def handleInt( self, _event=None ):
|
||||
"Handle control-c."
|
||||
self.node.sendInt()
|
||||
# pylint: enable-msg=W0613
|
||||
|
||||
def sendCmd( self, cmd ):
|
||||
"Send a command to our node."
|
||||
if not self.node.waiting:
|
||||
self.node.sendCmd( cmd )
|
||||
|
||||
# Callback ignores fds
|
||||
# pylint: disable-msg=W0613
|
||||
def handleReadable( self, fds, timeoutms=None ):
|
||||
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 )
|
||||
# pylint: enable-msg=W0613
|
||||
|
||||
def waiting( self ):
|
||||
"Are we waiting for output?"
|
||||
@@ -172,11 +169,8 @@ 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,):
|
||||
def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
|
||||
barwidth=10, ymax=3.5,):
|
||||
|
||||
Frame.__init__( self, parent )
|
||||
|
||||
@@ -198,7 +192,7 @@ class Graph( Frame ):
|
||||
width = 25
|
||||
ymax = self.ymax
|
||||
scale = Canvas( self, width=width, height=height,
|
||||
background=self.bg )
|
||||
background=self.bg )
|
||||
opts = { 'fill': 'red' }
|
||||
# Draw scale line
|
||||
scale.create_line( width - 1, height, width - 1, 0, **opts )
|
||||
@@ -214,7 +208,7 @@ class Graph( Frame ):
|
||||
ofs = 20
|
||||
height = self.gheight + ofs
|
||||
self.graph.configure( scrollregion=( 0, -ofs,
|
||||
self.xpos * self.barwidth, height ) )
|
||||
self.xpos * self.barwidth, height ) )
|
||||
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
|
||||
|
||||
def yview( self, *args ):
|
||||
@@ -234,7 +228,7 @@ class Graph( Frame ):
|
||||
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 ) )
|
||||
scrollregion=(0, 0, width, height ) )
|
||||
scale.configure( yscrollcommand=ybar.set )
|
||||
|
||||
# Layout
|
||||
@@ -255,7 +249,7 @@ class Graph( Frame ):
|
||||
x1 = x0 + self.barwidth
|
||||
y0 = self.gheight
|
||||
y1 = ( 1 - percent ) * self.gheight
|
||||
c.create_rectangle( x0 , y0, x1, y1, fill='green' )
|
||||
c.create_rectangle( x0, y0, x1, y1, fill='green' )
|
||||
self.xpos += 1
|
||||
self.updateScrollRegions()
|
||||
self.graph.xview( 'moveto', '1.0' )
|
||||
@@ -319,20 +313,25 @@ class ConsoleApp( Frame ):
|
||||
|
||||
self.pack( expand=True, fill='both' )
|
||||
|
||||
# Update callback doesn't use console arg
|
||||
# pylint: disable-msg=W0613
|
||||
def updateGraph( self, console, output ):
|
||||
def updateGraph( self, _console, output ):
|
||||
"Update our graph."
|
||||
m = re.search( r'(\d+) Mbits/sec', output )
|
||||
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 += .001 * float( m.group( 1 ) )
|
||||
self.bw += val
|
||||
if self.updates >= self.hostCount:
|
||||
self.graph.addBar( self.bw )
|
||||
self.bw = 0
|
||||
self.updates = 0
|
||||
# pylint: enable-msg=W0613
|
||||
|
||||
def setOutputHook( self, fn=None, consoles=None ):
|
||||
"Register fn as output hook [on specific consoles.]"
|
||||
@@ -419,7 +418,9 @@ class ConsoleApp( Frame ):
|
||||
count = len( consoles )
|
||||
self.setOutputHook( self.updateGraph )
|
||||
for console in consoles:
|
||||
console.node.cmd( 'iperf -sD' )
|
||||
# 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
|
||||
|
||||
+24
-52
@@ -1,64 +1,36 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example creates a multi-controller network from
|
||||
semi-scratch; note a topo object could also be used and
|
||||
would be passed into the Mininet() constructor.
|
||||
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 Controller, OVSKernelSwitch
|
||||
from mininet.cli import CLI
|
||||
from mininet.node import OVSSwitch, Controller, RemoteController
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.cli import CLI
|
||||
|
||||
Switch = OVSKernelSwitch
|
||||
setLogLevel( 'info' )
|
||||
|
||||
def addHost( net, N ):
|
||||
"Create host hN and add to net."
|
||||
name = 'h%d' % N
|
||||
ip = '10.0.0.%d' % N
|
||||
return net.addHost( name, ip=ip )
|
||||
# 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' )
|
||||
|
||||
def multiControllerNet():
|
||||
"Create a network with multiple controllers."
|
||||
cmap = { 's1': c0, 's2': c1, 's3': c2 }
|
||||
|
||||
net = Mininet( controller=Controller, switch=Switch)
|
||||
class MultiSwitch( OVSSwitch ):
|
||||
"Custom Switch() subclass that connects to different controllers"
|
||||
def start( self, controllers ):
|
||||
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
|
||||
|
||||
print "*** Creating 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 = [ addHost( net, n ) for n in 3, 4 ]
|
||||
hosts2 = [ addHost( net, n ) for n in 5, 6 ]
|
||||
|
||||
print "*** Creating links"
|
||||
for h in hosts1:
|
||||
s1.linkTo( h )
|
||||
for h in hosts2:
|
||||
s2.linkTo( h )
|
||||
s1.linkTo( 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()
|
||||
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
+151
@@ -0,0 +1,151 @@
|
||||
#!/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"""
|
||||
def checkListening( self ):
|
||||
"Ignore spurious error"
|
||||
pass
|
||||
|
||||
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
+81
@@ -0,0 +1,81 @@
|
||||
#!/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
|
||||
from mininet.log import setLogLevel, output
|
||||
|
||||
from time import sleep
|
||||
|
||||
def waitListening(client, server, port):
|
||||
"Wait until server is listening on port"
|
||||
if not client.cmd('which telnet'):
|
||||
raise Exception('Could not find telnet')
|
||||
cmd = ('sh -c "echo A | telnet -e A %s %s"' %
|
||||
(server.IP(), port))
|
||||
while 'Connected' not in client.cmd(cmd):
|
||||
output('waiting for', server,
|
||||
'to listen on port', port, '\n')
|
||||
sleep(.5)
|
||||
|
||||
|
||||
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 )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
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 )
|
||||
@@ -27,8 +27,8 @@ def emptyNet():
|
||||
s3 = net.addSwitch( 's3' )
|
||||
|
||||
info( '*** Creating links\n' )
|
||||
h1.linkTo( s3 )
|
||||
h2.linkTo( s3 )
|
||||
net.addLink( h1, s3 )
|
||||
net.addLink( h2, s3 )
|
||||
|
||||
info( '*** Starting network\n')
|
||||
net.start()
|
||||
|
||||
+15
-7
@@ -5,11 +5,12 @@ 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 re, 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
|
||||
|
||||
@@ -21,22 +22,29 @@ def checkIntf( intf ):
|
||||
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' )
|
||||
'and is probably in use!\n' )
|
||||
exit( 1 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
|
||||
newIntf = 'eth1'
|
||||
info( '*** Checking', newIntf, '\n' )
|
||||
checkIntf( newIntf )
|
||||
# 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', newIntf, 'to switch', switch.name, '\n' )
|
||||
switch.addIntf( newIntf )
|
||||
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 )
|
||||
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/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
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
|
||||
def testLinkLimit( net, bw ):
|
||||
"Run bandwidth limit test"
|
||||
print '*** Testing network %.2f Mbps bandwidth limit' % 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':
|
||||
print '*** Testing with', sched, 'bandwidth limiting'
|
||||
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()
|
||||
+30
-32
@@ -6,9 +6,9 @@ using both kernel and user datapaths.
|
||||
|
||||
We construct a network of N hosts and N-1 switches, connected as follows:
|
||||
|
||||
h1 <-> sN+1 <-> sN+2 .. sN+N-1
|
||||
| | |
|
||||
h2 h3 hN
|
||||
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
|
||||
@@ -23,42 +23,40 @@ of switches, this example demonstrates:
|
||||
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
import sys
|
||||
flush = sys.stdout.flush
|
||||
|
||||
from mininet.net import init, Mininet
|
||||
# from mininet.node import KernelSwitch
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch
|
||||
from mininet.topo import Topo, Node
|
||||
from mininet.log import lg
|
||||
|
||||
class LinearTestTopo( Topo ):
|
||||
"Topology for a string of N hosts and N-1 switches."
|
||||
|
||||
def __init__( self, N ):
|
||||
def __init__( self, N, **params ):
|
||||
|
||||
# Add default members to class.
|
||||
super( LinearTestTopo, self ).__init__()
|
||||
# Initialize topology
|
||||
Topo.__init__( self, **params )
|
||||
|
||||
# Create switch and host nodes
|
||||
hosts = range( 1, N + 1 )
|
||||
switches = range( N + 1 , N + N )
|
||||
for h in hosts:
|
||||
self.add_node( h, Node( is_switch=False ) )
|
||||
for s in switches:
|
||||
self.add_node( s, Node( is_switch=True ) )
|
||||
# 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
|
||||
for s in switches[ :-1 ]:
|
||||
self.add_edge( s, s + 1 )
|
||||
last = None
|
||||
for switch in switches:
|
||||
if last:
|
||||
self.addLink( last, switch )
|
||||
last = switch
|
||||
|
||||
# Wire up hosts
|
||||
self.add_edge( hosts[ 0 ], switches[ 0 ] )
|
||||
for h in hosts[ 1: ]:
|
||||
self.add_edge( h, h + N - 1 )
|
||||
|
||||
# Consider all switches and hosts 'on'
|
||||
self.enable_all()
|
||||
self.addLink( hosts[ 0 ], switches[ 0 ] )
|
||||
for host, switch in zip( hosts[ 1: ], switches ):
|
||||
self.addLink( host, switch )
|
||||
|
||||
|
||||
def linearBandwidthTest( lengths ):
|
||||
@@ -69,15 +67,16 @@ def linearBandwidthTest( lengths ):
|
||||
switchCount = max( lengths )
|
||||
hostCount = switchCount + 1
|
||||
|
||||
switches = { # 'reference kernel': KernelSwitch,
|
||||
'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
topo = LinearTestTopo( hostCount )
|
||||
|
||||
for datapath in switches.keys():
|
||||
print "*** testing", datapath, "datapath"
|
||||
Switch = switches[ datapath ]
|
||||
results[ datapath ] = []
|
||||
net = Mininet( topo=LinearTestTopo( hostCount ), switch=Switch )
|
||||
net = Mininet( topo=topo, switch=Switch, controller=Controller, waitConnected=True )
|
||||
net.start()
|
||||
print "*** testing basic connectivity"
|
||||
for n in lengths:
|
||||
@@ -106,7 +105,6 @@ def linearBandwidthTest( lengths ):
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info' )
|
||||
init()
|
||||
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
|
||||
print "*** Running linearBandwidthTest", sizes
|
||||
linearBandwidthTest( sizes )
|
||||
|
||||
+2976
-111
File diff suppressed because it is too large
Load Diff
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/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.util import quietRun
|
||||
from mininet.log import output, warn
|
||||
|
||||
from random import randint
|
||||
from re import findall
|
||||
|
||||
|
||||
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
+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()
|
||||
@@ -22,7 +22,7 @@ if __name__ == '__main__':
|
||||
info( "*** Initializing Mininet and kernel modules\n" )
|
||||
OVSKernelSwitch.setup()
|
||||
info( "*** Creating network\n" )
|
||||
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch)
|
||||
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
|
||||
info( "*** Starting network\n" )
|
||||
network.start()
|
||||
info( "*** Running ping test\n" )
|
||||
|
||||
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
+70
@@ -0,0 +1,70 @@
|
||||
#!/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, h=1, **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()
|
||||
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/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
|
||||
from mininet.node import Node
|
||||
|
||||
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 net():
|
||||
|
||||
"Create a network with 5 hosts."
|
||||
|
||||
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 )
|
||||
net.addLink( h5, s1, port1 = 1, port2 = 9 ) # specify a different port to connect host 5 to on the switch.
|
||||
|
||||
root = Node( 'root', inNamespace=False )
|
||||
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' )
|
||||
net()
|
||||
|
||||
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()
|
||||
+28
-18
@@ -8,13 +8,16 @@ but it exposes the configuration details and allows customization.
|
||||
For most tasks, the higher-level API will be preferable.
|
||||
"""
|
||||
|
||||
from mininet.net import init
|
||||
from mininet.node import Node, OVSKernelSwitch
|
||||
from mininet.util import createLink
|
||||
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
|
||||
|
||||
def scratchNet( cname='controller', cargs='ptcp:' ):
|
||||
"Create network from scratch using kernel switch."
|
||||
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 )
|
||||
@@ -23,36 +26,43 @@ def scratchNet( cname='controller', cargs='ptcp:' ):
|
||||
h1 = Node( 'h1' )
|
||||
|
||||
info( "*** Creating links\n" )
|
||||
createLink( node1=h0, node2=switch, port1=0, port2=0 )
|
||||
createLink( node1=h1, node2=switch, port1=0, port2=1 )
|
||||
Link( h0, switch )
|
||||
Link( h1, switch )
|
||||
|
||||
info( "*** Configuring hosts\n" )
|
||||
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', 24 )
|
||||
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
|
||||
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 kernel datapath\n" )
|
||||
info( "*** Starting network using Open vSwitch\n" )
|
||||
controller.cmd( cname + ' ' + cargs + '&' )
|
||||
switch.cmd( 'ovs-dpctl del-dp dp0' )
|
||||
switch.cmd( 'ovs-dpctl add-dp dp0' )
|
||||
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||
switch.cmd( 'ovs-vsctl add-br dp0' )
|
||||
for intf in switch.intfs.values():
|
||||
print switch.cmd( 'ovs-dpctl add-if dp0 ' + intf )
|
||||
print switch.cmd( 'ovs-openflowd dp0 tcp:127.0.0.1 &' )
|
||||
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-dpctl del-dp dp0' )
|
||||
switch.cmd( 'kill %ovs-openflowd' )
|
||||
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||
switch.deleteIntfs()
|
||||
info( '\n' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
info( '*** Scratch network demo (kernel datapath)\n' )
|
||||
OVSKernelSwitch.setup()
|
||||
init()
|
||||
Mininet.init()
|
||||
scratchNet()
|
||||
|
||||
+16
-11
@@ -10,11 +10,16 @@ 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 init
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.util import createLink
|
||||
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."
|
||||
|
||||
@@ -28,17 +33,17 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
switch = Node( 's0')
|
||||
h0 = Node( 'h0' )
|
||||
h1 = Node( 'h1' )
|
||||
cintf, sintf = createLink( controller, switch )
|
||||
h0intf, sintf1 = createLink( h0, switch )
|
||||
h1intf, sintf2 = createLink( h1, switch )
|
||||
cintf, sintf = linkIntfs( controller, switch )
|
||||
h0intf, sintf1 = linkIntfs( h0, switch )
|
||||
h1intf, sintf2 = linkIntfs( h1, switch )
|
||||
|
||||
info( '*** Configuring control network\n' )
|
||||
controller.setIP( cintf, '10.0.123.1', 24 )
|
||||
switch.setIP( sintf, '10.0.123.2', 24 )
|
||||
controller.setIP( '10.0.123.1/24', intf=cintf )
|
||||
switch.setIP( '10.0.123.2/24', intf=sintf)
|
||||
|
||||
info( '*** Configuring hosts\n' )
|
||||
h0.setIP( h0intf, '192.168.123.1', 24 )
|
||||
h1.setIP( h1intf, '192.168.123.2', 24 )
|
||||
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:
|
||||
@@ -47,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
info( '*** Starting controller and user datapath\n' )
|
||||
controller.cmd( cname + ' ' + cargs + '&' )
|
||||
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
||||
intfs = [ sintf1, sintf2 ]
|
||||
intfs = [ str( i ) for i in sintf1, sintf2 ]
|
||||
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
|
||||
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
|
||||
|
||||
@@ -64,5 +69,5 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
info( '*** Scratch network demo (user datapath)\n' )
|
||||
init()
|
||||
Mininet.init()
|
||||
scratchNetUser()
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/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)
|
||||
net.start()
|
||||
print "Dumping host connections"
|
||||
dumpNodeConnections(net.hosts)
|
||||
print "Testing network connectivity"
|
||||
net.pingAll()
|
||||
print "Testing bandwidth between h1 and h4"
|
||||
h1, h4 = net.getNodeByName('h1', 'h4')
|
||||
net.iperf((h1, h4))
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
perfTest()
|
||||
+25
-15
@@ -16,41 +16,47 @@ demonstrates:
|
||||
- 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, OVSKernelSwitch
|
||||
from mininet.node import Node
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import createLink
|
||||
from mininet.link import Link
|
||||
|
||||
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, prefixLen, routes ):
|
||||
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
|
||||
prefixLen: IP address prefix length (e.g. 8, 16, 24)
|
||||
routes: host networks to route to"""
|
||||
# Create a node in root namespace and link to switch 0
|
||||
root = Node( 'root', inNamespace=False )
|
||||
intf = createLink( root, switch )[ 0 ]
|
||||
root.setIP( intf, ip, prefixLen )
|
||||
intf = Link( 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 ' + intf )
|
||||
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
|
||||
|
||||
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
|
||||
"Start a network, connect it to root ns, and run sshd on all hosts."
|
||||
switch = network.switches[ 0 ] # switch to use
|
||||
ip = '10.123.123.1' # our IP address on host network
|
||||
routes = [ '10.0.0.0/8' ] # host networks to route to
|
||||
connectToRootNS( network, switch, ip, 8, routes )
|
||||
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
|
||||
@@ -67,5 +73,9 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4, switch=OVSKernelSwitch )
|
||||
sshd( net )
|
||||
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
|
||||
opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
|
||||
'-D -o UseDNS=no -u0' )
|
||||
sshd( net, opts=opts )
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/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
|
||||
|
||||
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
|
||||
unittest.TextTestRunner( 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
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for baresshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from time import sleep
|
||||
from mininet.clean import cleanup, sh
|
||||
|
||||
class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
opts = [ '\(yes/no\)\?', 'Welcome to h1', 'refused', pexpect.EOF, pexpect.TIMEOUT ]
|
||||
|
||||
def connected( self ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -i /tmp/ssh/test_rsa exit' )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
p.sendline( 'yes' )
|
||||
elif index == 1:
|
||||
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' )
|
||||
sh( cmd )
|
||||
|
||||
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
|
||||
for _ in range( 3 ):
|
||||
result = self.connected()
|
||||
if result:
|
||||
break
|
||||
else:
|
||||
sleep( 1 )
|
||||
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
+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()
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for controlnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testControlNet( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testPingall( self ):
|
||||
"Simple pingall test that verifies 0% packet drop in data network"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
|
||||
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()
|
||||
|
||||
def testFailover( self ):
|
||||
"Kill controllers and verify that switch, s1, fails over properly"
|
||||
count = 1
|
||||
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
|
||||
p.expect( self.prompt )
|
||||
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' )
|
||||
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
|
||||
ip = int( lp.match.group( 1 ) )
|
||||
self.assertEqual( count, ip )
|
||||
count += 1
|
||||
for c in [ 'c0', 'c1' ]:
|
||||
p.sendline( '%s ifconfig %s-eth0 down' % ( c, c) )
|
||||
p.expect( self.prompt )
|
||||
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
|
||||
ip = int( lp.match.group( 1 ) )
|
||||
self.assertEqual( count, ip )
|
||||
count += 1
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for cpu.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testCPU( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testCPU( self ):
|
||||
"Verify that CPU utilization is monotonically decreasing for each scheduler"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
|
||||
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)', pexpect.EOF ]
|
||||
scheds = []
|
||||
while True:
|
||||
index = p.expect( opts, timeout=600 )
|
||||
if index == 0:
|
||||
sched = p.match.group( 1 )
|
||||
cpu = float( p.match.group( 2 ) )
|
||||
bw = float( p.match.group( 3 ) )
|
||||
if sched not in scheds:
|
||||
scheds.append( sched )
|
||||
previous_bw = 10 ** 4 # 10 GB/s
|
||||
self.assertTrue( bw < previous_bw )
|
||||
previous_bw = bw
|
||||
else:
|
||||
break
|
||||
|
||||
self.assertTrue( len( scheds ) > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for emptynet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testEmptyNet( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testEmptyNet( self ):
|
||||
"Run simple CLI tests: pingall (verify 0% drop) and iperf (sanity)"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.emptynet' )
|
||||
p.expect( self.prompt )
|
||||
# pingall 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 )
|
||||
# iperf test
|
||||
p.sendline( 'iperf' )
|
||||
p.expect( "Results: \['[\d.]+ .bits/sec', '[\d.]+ .bits/sec'\]" )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for hwintf.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import re
|
||||
|
||||
import pexpect
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import Node
|
||||
from mininet.link import Link
|
||||
|
||||
|
||||
class testHwintf( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def setUp( self ):
|
||||
self.h3 = Node( 't0', ip='10.0.0.3/8' )
|
||||
self.n0 = Node( 't1', inNamespace=False )
|
||||
Link( self.h3, self.n0 )
|
||||
self.h3.configDefault()
|
||||
|
||||
def testLocalPing( self ):
|
||||
"Verify connectivity between virtual hosts using pingall"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() )
|
||||
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()
|
||||
|
||||
def testExternalPing( self ):
|
||||
"Verify connnectivity between virtual host and virtual-physical 'external' host "
|
||||
p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() )
|
||||
p.expect( self.prompt )
|
||||
# test ping external to internal
|
||||
expectStr = '(\d+) packets transmitted, (\d+) received'
|
||||
m = re.search( expectStr, self.h3.cmd( 'ping -v -c 1 10.0.0.1' ) )
|
||||
tx = m.group( 1 )
|
||||
rx = m.group( 2 )
|
||||
self.assertEqual( tx, rx )
|
||||
# test ping internal to external
|
||||
p.sendline( 'h1 ping -c 1 10.0.0.3')
|
||||
p.expect( expectStr )
|
||||
tx = p.match.group( 1 )
|
||||
rx = p.match.group( 2 )
|
||||
self.assertEqual( tx, rx )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def tearDown( self ):
|
||||
self.h3.terminate()
|
||||
self.n0.terminate()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for limit.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testLimit( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testLimit( self ):
|
||||
"Verify that CPU limits are within a 2% tolerance of limit for each scheduler"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.limit' )
|
||||
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
|
||||
'\*\*\* Results: \[([\d\., ]+)\]',
|
||||
pexpect.EOF ]
|
||||
count = 0
|
||||
bw = 0
|
||||
tolerance = 2
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
bw = float( p.match.group( 1 ) )
|
||||
count += 1
|
||||
elif index == 1:
|
||||
results = p.match.group( 1 )
|
||||
for x in results.split( ',' ):
|
||||
result = float( x )
|
||||
self.assertTrue( result < bw + tolerance )
|
||||
self.assertTrue( result > bw - tolerance )
|
||||
else:
|
||||
break
|
||||
|
||||
self.assertTrue( count > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for linearbandwidth.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testLinearBandwidth( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testLinearBandwidth( self ):
|
||||
"Verify that bandwidth is monotonically decreasing as # of hops increases"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
|
||||
count = 0
|
||||
opts = [ '\*\*\* Linear network results',
|
||||
'(\d+)\s+([\d\.]+) (.bits)',
|
||||
pexpect.EOF ]
|
||||
while True:
|
||||
index = p.expect( opts, timeout=600 )
|
||||
if index == 0:
|
||||
previous_bw = 10 ** 10 # 10 Gbits
|
||||
count += 1
|
||||
elif index == 1:
|
||||
n = int( p.match.group( 1 ) )
|
||||
bw = float( p.match.group( 2 ) )
|
||||
unit = p.match.group( 3 )
|
||||
if unit[ 0 ] == 'K':
|
||||
bw *= 10 ** 3
|
||||
elif unit[ 0 ] == 'M':
|
||||
bw *= 10 ** 6
|
||||
elif unit[ 0 ] == 'G':
|
||||
bw *= 10 ** 9
|
||||
self.assertTrue( bw < previous_bw )
|
||||
previous_bw = bw
|
||||
else:
|
||||
break
|
||||
|
||||
self.assertTrue( count > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for mobility.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from subprocess import check_output
|
||||
|
||||
class testMobility( unittest.TestCase ):
|
||||
|
||||
def testMobility( self ):
|
||||
"Run the example and verify its 4 ping results"
|
||||
cmd = 'python -m mininet.examples.mobility 2>&1'
|
||||
grep = ' | grep -c " 0% dropped" '
|
||||
result = check_output( cmd + grep, shell=True )
|
||||
assert int( result ) == 4
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for multiping.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from collections import defaultdict
|
||||
|
||||
class testMultiPing( unittest.TestCase ):
|
||||
|
||||
def testMultiPing( self ):
|
||||
"""Verify that each target is pinged at least once, and
|
||||
that pings to 'real' targets are successful and unknown targets fail"""
|
||||
p = pexpect.spawn( 'python -m mininet.examples.multiping' )
|
||||
opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)",
|
||||
"(h\d+): ([\d.]+) -> ([\d.]+) \d packets transmitted, (\d) received",
|
||||
pexpect.EOF ]
|
||||
pings = defaultdict( list )
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
name = p.match.group(1)
|
||||
ip = p.match.group(2)
|
||||
targets = p.match.group(3).split()
|
||||
pings[ name ] += targets
|
||||
elif index == 1:
|
||||
name = p.match.group(1)
|
||||
ip = p.match.group(2)
|
||||
target = p.match.group(3)
|
||||
received = int( p.match.group(4) )
|
||||
if target == '10.0.0.200':
|
||||
self.assertEqual( received, 0 )
|
||||
else:
|
||||
self.assertEqual( received, 1 )
|
||||
try:
|
||||
pings[ name ].remove( target )
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
self.assertTrue( len( pings ) > 0 )
|
||||
for t in pings.values():
|
||||
self.assertEqual( len( t ), 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for multipoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testMultiPoll( unittest.TestCase ):
|
||||
|
||||
def testMultiPoll( self ):
|
||||
"Verify that we receive one ping per second per host"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.multipoll' )
|
||||
opts = [ "\*\*\* (h\d) :" ,
|
||||
"(h\d+): \d+ bytes from",
|
||||
"Monitoring output for (\d+) seconds",
|
||||
pexpect.EOF ]
|
||||
pings = {}
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
name = p.match.group( 1 )
|
||||
pings[ name ] = 0
|
||||
elif index == 1:
|
||||
name = p.match.group( 1 )
|
||||
pings[ name ] += 1
|
||||
elif index == 2:
|
||||
seconds = int( p.match.group( 1 ) )
|
||||
else:
|
||||
break
|
||||
self.assertTrue( len( pings ) > 0 )
|
||||
# make sure we have received at least one ping per second
|
||||
for count in pings.values():
|
||||
self.assertTrue( count >= seconds )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for multitest.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testMultiTest( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testMultiTest( self ):
|
||||
"Verify pingall (0% dropped) and hX-eth0 interface for each host (ifconfig)"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.multitest' )
|
||||
p.expect( '(\d+)% dropped' )
|
||||
dropped = int( p.match.group( 1 ) )
|
||||
self.assertEqual( dropped, 0 )
|
||||
ifCount = 0
|
||||
while True:
|
||||
index = p.expect( [ 'h\d-eth0', self.prompt ] )
|
||||
if index == 0:
|
||||
ifCount += 1
|
||||
elif index == 1:
|
||||
p.sendline( 'exit' )
|
||||
break
|
||||
p.wait()
|
||||
self.assertEqual( ifCount, 4 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for nat.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
destIP = '8.8.8.8' # Google DNS
|
||||
|
||||
class testNAT( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
|
||||
'Destination IP is not reachable' )
|
||||
def testNAT( self ):
|
||||
"Attempt to ping an IP on the Internet and verify 0% packet loss"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.nat' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 %s' % destIP )
|
||||
p.expect ( '(\d+)% packet loss' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( percent, 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for natnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testNATNet( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def setUp( self ):
|
||||
self.net = pexpect.spawn( 'python -m mininet.examples.natnet' )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testPublicPing( self ):
|
||||
"Attempt to ping the public server (h0) from h1 and h2"
|
||||
self.net.sendline( 'h1 ping -c 1 h0' )
|
||||
self.net.expect ( '(\d+)% packet loss' )
|
||||
percent = int( self.net.match.group( 1 ) ) if self.net.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
self.net.sendline( 'h2 ping -c 1 h0' )
|
||||
self.net.expect ( '(\d+)% packet loss' )
|
||||
percent = int( self.net.match.group( 1 ) ) if self.net.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testPrivatePing( self ):
|
||||
"Attempt to ping h1 and h2 from public server"
|
||||
self.net.sendline( 'h0 ping -c 1 -t 1 h1' )
|
||||
result = self.net.expect ( [ 'unreachable', 'loss' ] )
|
||||
self.assertEqual( result, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
self.net.sendline( 'h0 ping -c 1 -t 1 h2' )
|
||||
result = self.net.expect ( [ 'unreachable', 'loss' ] )
|
||||
self.assertEqual( result, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testPrivateToPrivatePing( self ):
|
||||
"Attempt to ping from NAT'ed host h1 to NAT'ed host h2"
|
||||
self.net.sendline( 'h1 ping -c 1 -t 1 h2' )
|
||||
result = self.net.expect ( [ '[Uu]nreachable', 'loss' ] )
|
||||
self.assertEqual( result, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def tearDown( self ):
|
||||
self.net.sendline( 'exit' )
|
||||
self.net.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for numberedports.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from collections import defaultdict
|
||||
from mininet.node import OVSSwitch
|
||||
|
||||
class testNumberedports( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( OVSSwitch.setup() or OVSSwitch.isOldOVS(), "old version of OVS" )
|
||||
def testConsistency( self ):
|
||||
"""verify consistency between mininet and ovs ports"""
|
||||
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
|
||||
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
|
||||
'Validating that s1-eth\d is actually on port \d ... WARNING',
|
||||
pexpect.EOF ]
|
||||
correct_ports = True
|
||||
count = 0
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
count += 1
|
||||
elif index == 1:
|
||||
correct_ports = False
|
||||
elif index == 2:
|
||||
self.assertNotEqual( 0, count )
|
||||
break
|
||||
self.assertTrue( correct_ports )
|
||||
|
||||
def testNumbering( self ):
|
||||
"""verify that all of the port numbers are printed correctly and consistent with their interface"""
|
||||
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
|
||||
opts = [ 's1-eth(\d+) : (\d+)',
|
||||
pexpect.EOF ]
|
||||
count_intfs = 0
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
count_intfs += 1
|
||||
intfport = p.match.group( 1 )
|
||||
ofport = p.match.group( 2 )
|
||||
self.assertEqual( intfport, ofport )
|
||||
elif index == 1:
|
||||
break
|
||||
self.assertNotEqual( 0, count_intfs )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for popen.py and popenpoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testPopen( unittest.TestCase ):
|
||||
|
||||
def pingTest( self, name ):
|
||||
"Verify that there are no dropped packets for each host"
|
||||
p = pexpect.spawn( 'python -m %s' % name )
|
||||
opts = [ "<(h\d+)>: PING ",
|
||||
"<(h\d+)>: (\d+) packets transmitted, (\d+) received",
|
||||
pexpect.EOF ]
|
||||
pings = {}
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
name = p.match.group(1)
|
||||
pings[ name ] = 0
|
||||
elif index == 1:
|
||||
name = p.match.group(1)
|
||||
transmitted = p.match.group(2)
|
||||
received = p.match.group(3)
|
||||
# verify no dropped packets
|
||||
self.assertEqual( received, transmitted )
|
||||
pings[ name ] += 1
|
||||
else:
|
||||
break
|
||||
self.assertTrue( len(pings) > 0 )
|
||||
# verify that each host has gotten results
|
||||
for count in pings.values():
|
||||
self.assertEqual( count, 1 )
|
||||
|
||||
def testPopen( self ):
|
||||
self.pingTest( 'mininet.examples.popen' )
|
||||
|
||||
def testPopenPoll( self ):
|
||||
self.pingTest( 'mininet.examples.popenpoll' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for scratchnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
|
||||
class testScratchNet( unittest.TestCase ):
|
||||
|
||||
opts = [ "1 packets transmitted, 1 received, 0% packet loss", pexpect.EOF ]
|
||||
|
||||
def pingTest( self, name ):
|
||||
"Verify that no ping packets were dropped"
|
||||
p = pexpect.spawn( 'python -m %s' % name )
|
||||
index = p.expect( self.opts )
|
||||
self.assertEqual( index, 0 )
|
||||
|
||||
def testPingKernel( self ):
|
||||
self.pingTest( 'mininet.examples.scratchnet' )
|
||||
|
||||
def testPingUser( self ):
|
||||
self.pingTest( 'mininet.examples.scratchnetuser' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for simpleperf.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import re
|
||||
import sys
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.link import TCLink
|
||||
|
||||
from mininet.examples.simpleperf import SingleSwitchTopo
|
||||
|
||||
class testSimplePerf( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testE2E( self ):
|
||||
"Run the example and verify ping and iperf results"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
|
||||
# check ping results
|
||||
p.expect( "Results: (\d+)% dropped", timeout=120 )
|
||||
loss = int( p.match.group( 1 ) )
|
||||
self.assertTrue( 0 < loss < 100 )
|
||||
# check iperf results
|
||||
p.expect( "Results: \['([\d\.]+) .bits/sec", timeout=480 )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw > 0 )
|
||||
p.wait()
|
||||
|
||||
def testTopo( self ):
|
||||
"""Import SingleSwitchTopo from example and test connectivity between two hosts
|
||||
Note: this test may fail very rarely because it is non-deterministic
|
||||
i.e. links are configured with 10% packet loss, but if we get unlucky and
|
||||
none or all of the packets are dropped, the test will fail"""
|
||||
topo = SingleSwitchTopo( n=4 )
|
||||
net = Mininet( topo=topo, host=CPULimitedHost, link=TCLink )
|
||||
net.start()
|
||||
h1, h4 = net.get( 'h1', 'h4' )
|
||||
# have h1 ping h4 ten times
|
||||
expectStr = '(\d+) packets transmitted, (\d+) received, (\d+)% packet loss'
|
||||
output = h1.cmd( 'ping -c 10 %s' % h4.IP() )
|
||||
m = re.search( expectStr, output )
|
||||
loss = int( m.group( 3 ) )
|
||||
net.stop()
|
||||
self.assertTrue( 0 < loss < 100 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for sshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.clean import sh
|
||||
|
||||
class testSSHD( unittest.TestCase ):
|
||||
|
||||
opts = [ '\(yes/no\)\?', 'refused', 'Welcome|\$|#', pexpect.EOF, pexpect.TIMEOUT ]
|
||||
|
||||
def connected( self, ip ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
# Note: this test will fail if "Welcome" is not in the sshd banner
|
||||
# and '#'' or '$'' are not in the prompt
|
||||
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
print p.match.group(0)
|
||||
p.sendline( 'yes' )
|
||||
elif index == 1:
|
||||
return False
|
||||
elif index == 2:
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def setUp( self ):
|
||||
# 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' )
|
||||
cmd = ( 'python -m mininet.examples.sshd -D '
|
||||
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
|
||||
'-o StrictModes=no -o UseDNS=no -u0' )
|
||||
# run example with custom sshd args
|
||||
self.net = pexpect.spawn( cmd )
|
||||
self.net.expect( 'mininet>' )
|
||||
|
||||
def testSSH( self ):
|
||||
"Verify that we can ssh into all hosts (h1 to h4)"
|
||||
for h in range( 1, 5 ):
|
||||
self.assertTrue( self.connected( '10.0.0.%d' % h ) )
|
||||
|
||||
def tearDown( self ):
|
||||
self.net.sendline( 'exit' )
|
||||
self.net.wait()
|
||||
# remove public key pair
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for tree1024.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testTree1024( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testTree1024( self ):
|
||||
"Run the example and do a simple ping test from h1 to h1024"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
|
||||
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
|
||||
p.sendline( 'h1 ping -c 1 h1024' )
|
||||
p.expect ( '(\d+)% packet loss' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( percent, 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for treeping64.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testTreePing64( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testTreePing64( self ):
|
||||
"Run the example and verify ping results"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.treeping64' )
|
||||
p.expect( 'Tree network ping results:', timeout=6000 )
|
||||
count = 0
|
||||
while True:
|
||||
index = p.expect( [ '(\d+)% packet loss', pexpect.EOF ] )
|
||||
if index == 0:
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
count += 1
|
||||
else:
|
||||
break
|
||||
self.assertTrue( count > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -11,8 +11,8 @@ def treePing64():
|
||||
|
||||
results = {}
|
||||
switches = { # 'reference kernel': KernelSwitch,
|
||||
'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
for name in switches:
|
||||
print "*** Testing", name, "datapath"
|
||||
|
||||
+39
-7
@@ -10,7 +10,8 @@ It may also get rid of 'false positives', but hopefully
|
||||
nothing irreplaceable!
|
||||
"""
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
from subprocess import Popen, PIPE, check_output as co
|
||||
import time
|
||||
|
||||
from mininet.log import info
|
||||
from mininet.term import cleanUpScreens
|
||||
@@ -27,28 +28,59 @@ def cleanup():
|
||||
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
|
||||
"\n")
|
||||
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
||||
zombies += 'ovs-openflowd udpbwtest'
|
||||
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
|
||||
# Note: real zombie processes can't actually be killed, since they
|
||||
# are already (un)dead. Then again,
|
||||
# you can't connect to them either, so they're mostly harmless.
|
||||
# Send SIGTERM first to give processes a chance to shutdown cleanly.
|
||||
sh( 'killall ' + zombies + ' 2> /dev/null' )
|
||||
time.sleep(1)
|
||||
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
||||
|
||||
# And kill off sudo mnexec
|
||||
sh( 'pkill -9 -f "sudo mnexec"')
|
||||
|
||||
info( "*** Removing junk from /tmp\n" )
|
||||
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
|
||||
|
||||
info( "*** Removing old screen sessions\n" )
|
||||
info( "*** Removing old X11 tunnels\n" )
|
||||
cleanUpScreens()
|
||||
|
||||
info( "*** Removing excess kernel datapaths\n" )
|
||||
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' )
|
||||
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).splitlines()
|
||||
for dp in dps:
|
||||
if dp != '':
|
||||
if dp:
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
|
||||
info( "*** Removing OVS datapaths" )
|
||||
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
|
||||
if dps:
|
||||
sh( "ovs-vsctl " + " -- ".join( "--if-exists del-br " + dp
|
||||
for dp in dps if dp ) )
|
||||
# And in case the above didn't work...
|
||||
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
|
||||
for dp in dps:
|
||||
sh( 'ovs-vsctl del-br ' + dp )
|
||||
|
||||
info( "*** Removing all links of the pattern foo-ethX\n" )
|
||||
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
|
||||
links = sh( "ip link show | "
|
||||
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'" ).splitlines()
|
||||
for link in links:
|
||||
if link != '':
|
||||
if link:
|
||||
sh( "ip link del " + link )
|
||||
|
||||
info( "*** Killing stale mininet node processes\n" )
|
||||
sh( 'pkill -9 -f mininet:' )
|
||||
# Make sure they are gone
|
||||
while True:
|
||||
try:
|
||||
pids = co( 'pgrep -f mininet:'.split() )
|
||||
except:
|
||||
pids = ''
|
||||
if pids:
|
||||
sh( 'pkill -f 9 mininet:' )
|
||||
sleep( .5 )
|
||||
else:
|
||||
break
|
||||
|
||||
info( "*** Cleanup complete.\n" )
|
||||
|
||||
+113
-63
@@ -30,10 +30,13 @@ from cmd import Cmd
|
||||
from os import isatty
|
||||
from select import poll, POLLIN
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import atexit
|
||||
|
||||
from mininet.log import info, output, error
|
||||
from mininet.term import makeTerms
|
||||
from mininet.util import quietRun, isShellBuiltin
|
||||
from mininet.term import makeTerms, runX11
|
||||
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
|
||||
|
||||
class CLI( Cmd ):
|
||||
"Simple command-line interface to talk to nodes."
|
||||
@@ -42,10 +45,8 @@ class CLI( Cmd ):
|
||||
|
||||
def __init__( self, mininet, stdin=sys.stdin, script=None ):
|
||||
self.mn = mininet
|
||||
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
|
||||
self.nodemap = {} # map names to Node objects
|
||||
for node in self.nodelist:
|
||||
self.nodemap[ node.name ] = node
|
||||
# Local variable bindings for py command
|
||||
self.locals = { 'net': mininet }
|
||||
# Attempt to handle input
|
||||
self.stdin = stdin
|
||||
self.inPoller = poll()
|
||||
@@ -53,18 +54,30 @@ class CLI( Cmd ):
|
||||
self.inputFile = script
|
||||
Cmd.__init__( self )
|
||||
info( '*** Starting CLI:\n' )
|
||||
|
||||
# Set up history if readline is available
|
||||
try:
|
||||
import readline
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
history_path = os.path.expanduser('~/.mininet_history')
|
||||
if os.path.isfile(history_path):
|
||||
readline.read_history_file(history_path)
|
||||
atexit.register(lambda: readline.write_history_file(history_path))
|
||||
|
||||
if self.inputFile:
|
||||
self.do_source( self.inputFile )
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
# Make sure no nodes are still waiting
|
||||
for node in self.nodelist:
|
||||
for node in self.mn.values():
|
||||
while node.waiting:
|
||||
node.sendInt()
|
||||
node.monitor()
|
||||
if self.isatty():
|
||||
quietRun( 'stty sane' )
|
||||
quietRun( 'stty echo sane intr "^C"' )
|
||||
self.cmdloop()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
@@ -74,10 +87,15 @@ class CLI( Cmd ):
|
||||
"Don't repeat last command when you hit return."
|
||||
pass
|
||||
|
||||
def getLocals( self ):
|
||||
"Local variable bindings for py command"
|
||||
self.locals.update( self.mn )
|
||||
return self.locals
|
||||
|
||||
# Disable pylint "Unused argument: 'arg's'" messages, as well as
|
||||
# "method could be a function" warning, since each CLI function
|
||||
# must have the same interface
|
||||
# pylint: disable-msg=W0613,R0201
|
||||
# pylint: disable-msg=R0201
|
||||
|
||||
helpStr = (
|
||||
'You may also send a command to a node using:\n'
|
||||
@@ -104,34 +122,27 @@ class CLI( Cmd ):
|
||||
if line is '':
|
||||
output( self.helpStr )
|
||||
|
||||
def do_nodes( self, line ):
|
||||
def do_nodes( self, _line ):
|
||||
"List all nodes."
|
||||
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
|
||||
nodes = ' '.join( sorted( self.mn ) )
|
||||
output( 'available nodes are: \n%s\n' % nodes )
|
||||
|
||||
def do_net( self, line ):
|
||||
def do_net( self, _line ):
|
||||
"List network connections."
|
||||
for switch in self.mn.switches:
|
||||
output( switch.name, '<->' )
|
||||
for intf in switch.intfs.values():
|
||||
# Ugly, but pylint wants it
|
||||
name = switch.connection.get( intf,
|
||||
( None, 'Unknown ' ) )[ 1 ]
|
||||
output( ' %s' % name )
|
||||
output( '\n' )
|
||||
dumpNodeConnections( self.mn.values() )
|
||||
|
||||
def do_sh( self, line ):
|
||||
"Run an external shell command"
|
||||
call( line, shell=True )
|
||||
|
||||
# do_py() needs to catch any exception during eval()
|
||||
# do_py() and do_px() need to catch any exception during eval()/exec()
|
||||
# pylint: disable-msg=W0703
|
||||
|
||||
def do_py( self, line ):
|
||||
"""Evaluate a Python expression.
|
||||
Node names may be used, e.g.: h1.cmd('ls')"""
|
||||
Node names may be used, e.g.: py h1.cmd('ls')"""
|
||||
try:
|
||||
result = eval( line, globals(), self.nodemap )
|
||||
result = eval( line, globals(), self.getLocals() )
|
||||
if not result:
|
||||
return
|
||||
elif isinstance( result, str ):
|
||||
@@ -141,16 +152,35 @@ class CLI( Cmd ):
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# pylint: enable-msg=W0703
|
||||
# We are in fact using the exec() pseudo-function
|
||||
# pylint: disable-msg=W0122
|
||||
|
||||
def do_px( self, line ):
|
||||
"""Execute a Python statement.
|
||||
Node names may be used, e.g.: px print h1.cmd('ls')"""
|
||||
try:
|
||||
exec( line, globals(), self.getLocals() )
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# pylint: enable-msg=W0703,W0122
|
||||
|
||||
def do_pingall( self, line ):
|
||||
"Ping between all hosts."
|
||||
self.mn.pingAll()
|
||||
self.mn.pingAll( line )
|
||||
|
||||
def do_pingpair( self, line ):
|
||||
def do_pingpair( self, _line ):
|
||||
"Ping between first two hosts, useful for testing."
|
||||
self.mn.pingPair()
|
||||
|
||||
def do_pingallfull( self, _line ):
|
||||
"Ping between all hosts, returns all ping results."
|
||||
self.mn.pingAllFull()
|
||||
|
||||
def do_pingpairfull( self, _line ):
|
||||
"Ping between first two hosts, returns all ping results."
|
||||
self.mn.pingPairFull()
|
||||
|
||||
def do_iperf( self, line ):
|
||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||
args = line.split()
|
||||
@@ -160,18 +190,18 @@ class CLI( Cmd ):
|
||||
hosts = []
|
||||
err = False
|
||||
for arg in args:
|
||||
if arg not in self.nodemap:
|
||||
if arg not in self.mn:
|
||||
err = True
|
||||
error( "node '%s' not in network\n" % arg )
|
||||
else:
|
||||
hosts.append( self.nodemap[ arg ] )
|
||||
hosts.append( self.mn[ arg ] )
|
||||
if not err:
|
||||
self.mn.iperf( hosts )
|
||||
else:
|
||||
error( 'invalid number of args: iperf src dst\n' )
|
||||
|
||||
def do_iperfudp( self, line ):
|
||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||
"Simple iperf UDP test between two (optionally specified) hosts."
|
||||
args = line.split()
|
||||
if not args:
|
||||
self.mn.iperf( l4Type='UDP' )
|
||||
@@ -180,27 +210,27 @@ class CLI( Cmd ):
|
||||
hosts = []
|
||||
err = False
|
||||
for arg in args[ 1:3 ]:
|
||||
if arg not in self.nodemap:
|
||||
if arg not in self.mn:
|
||||
err = True
|
||||
error( "node '%s' not in network\n" % arg )
|
||||
else:
|
||||
hosts.append( self.nodemap[ arg ] )
|
||||
hosts.append( self.mn[ arg ] )
|
||||
if not err:
|
||||
self.mn.iperf( hosts, l4Type='UDP', udpBw=udpBw )
|
||||
else:
|
||||
error( 'invalid number of args: iperfudp bw src dst\n' +
|
||||
'bw examples: 10M\n' )
|
||||
|
||||
def do_intfs( self, line ):
|
||||
def do_intfs( self, _line ):
|
||||
"List interfaces."
|
||||
for node in self.nodelist:
|
||||
for node in self.mn.values():
|
||||
output( '%s: %s\n' %
|
||||
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
|
||||
( node.name, ','.join( node.intfNames() ) ) )
|
||||
|
||||
def do_dump( self, line ):
|
||||
def do_dump( self, _line ):
|
||||
"Dump node info."
|
||||
for node in self.nodelist:
|
||||
output( '%s\n' % node )
|
||||
for node in self.mn.values():
|
||||
output( '%s\n' % repr( node ) )
|
||||
|
||||
def do_link( self, line ):
|
||||
"Bring link(s) between two nodes up or down."
|
||||
@@ -219,17 +249,28 @@ class CLI( Cmd ):
|
||||
error( 'usage: %s node1 node2 ...\n' % term )
|
||||
else:
|
||||
for arg in args:
|
||||
if arg not in self.nodemap:
|
||||
if arg not in self.mn:
|
||||
error( "node '%s' not in network\n" % arg )
|
||||
else:
|
||||
node = self.nodemap[ arg ]
|
||||
node = self.mn[ arg ]
|
||||
self.mn.terms += makeTerms( [ node ], term = term )
|
||||
|
||||
def do_x( self, line ):
|
||||
"""Create an X11 tunnel to the given node,
|
||||
optionally starting a client."""
|
||||
args = line.split()
|
||||
if not args:
|
||||
error( 'usage: x node [cmd args]...\n' )
|
||||
else:
|
||||
node = self.mn[ args[ 0 ] ]
|
||||
cmd = args[ 1: ]
|
||||
self.mn.terms += runX11( node, cmd )
|
||||
|
||||
def do_gterm( self, line ):
|
||||
"Spawn gnome-terminal(s) for the given node(s)."
|
||||
self.do_xterm( line, term='gterm' )
|
||||
|
||||
def do_exit( self, line ):
|
||||
def do_exit( self, _line ):
|
||||
"Exit"
|
||||
return 'exited by user command'
|
||||
|
||||
@@ -270,21 +311,25 @@ class CLI( Cmd ):
|
||||
break
|
||||
except IOError:
|
||||
error( 'error reading file %s\n' % args[ 0 ] )
|
||||
self.inputFile.close()
|
||||
self.inputFile = None
|
||||
|
||||
def do_dpctl( self, line ):
|
||||
"Run dpctl command on all switches."
|
||||
"Run dpctl (or ovs-ofctl) command on all switches."
|
||||
args = line.split()
|
||||
if len(args) == 0:
|
||||
if len(args) < 1:
|
||||
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
|
||||
return
|
||||
if not self.mn.listenPort:
|
||||
error( "can't run dpctl w/no passive listening port\n")
|
||||
return
|
||||
for sw in self.mn.switches:
|
||||
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
|
||||
output( sw.cmd( 'dpctl ' + ' '.join(args) +
|
||||
' tcp:127.0.0.1:%i' % sw.listenPort ) )
|
||||
output( sw.dpctl( *args ) )
|
||||
|
||||
def do_time( self, line ):
|
||||
"Measure time taken for any command in Mininet."
|
||||
start = time.time()
|
||||
self.onecmd(line)
|
||||
elapsed = time.time() - start
|
||||
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
|
||||
|
||||
def default( self, line ):
|
||||
"""Called on an input line when the command prefix is not recognized.
|
||||
@@ -293,34 +338,35 @@ class CLI( Cmd ):
|
||||
corresponding IP addrs."""
|
||||
|
||||
first, args, line = self.parseline( line )
|
||||
if args and len(args) > 0 and args[ -1 ] == '\n':
|
||||
args = args[ :-1 ]
|
||||
rest = args.split( ' ' )
|
||||
|
||||
if first in self.nodemap:
|
||||
node = self.nodemap[ first ]
|
||||
if first in self.mn:
|
||||
if not args:
|
||||
print "*** Enter a command for node: %s <cmd>" % first
|
||||
return
|
||||
node = self.mn[ first ]
|
||||
rest = args.split( ' ' )
|
||||
# Substitute IP addresses for node names in command
|
||||
rest = [ self.nodemap[ arg ].IP()
|
||||
if arg in self.nodemap else arg
|
||||
for arg in rest ]
|
||||
# If updateIP() returns None, then use node name
|
||||
rest = [ self.mn[ arg ].defaultIntf().updateIP() or arg
|
||||
if arg in self.mn else arg
|
||||
for arg in rest ]
|
||||
rest = ' '.join( rest )
|
||||
# Run cmd on node:
|
||||
builtin = isShellBuiltin( first )
|
||||
node.sendCmd( rest, printPid=( not builtin ) )
|
||||
node.sendCmd( rest )
|
||||
self.waitForNode( node )
|
||||
else:
|
||||
error( '*** Unknown command: %s\n' % first )
|
||||
error( '*** Unknown command: %s\n' % line )
|
||||
|
||||
# pylint: enable-msg=W0613,R0201
|
||||
# pylint: enable-msg=R0201
|
||||
|
||||
def waitForNode( self, node ):
|
||||
"Wait for a node to finish, and print its output."
|
||||
"Wait for a node to finish, and print its output."
|
||||
# Pollers
|
||||
nodePoller = poll()
|
||||
nodePoller.register( node.stdout )
|
||||
bothPoller = poll()
|
||||
bothPoller.register( self.stdin )
|
||||
bothPoller.register( node.stdout )
|
||||
bothPoller.register( self.stdin, POLLIN )
|
||||
bothPoller.register( node.stdout, POLLIN )
|
||||
if self.isatty():
|
||||
# Buffer by character, so that interactive
|
||||
# commands sort of work
|
||||
@@ -332,7 +378,7 @@ class CLI( Cmd ):
|
||||
if False and self.inputFile:
|
||||
key = self.inputFile.read( 1 )
|
||||
if key is not '':
|
||||
node.write(key)
|
||||
node.write( key )
|
||||
else:
|
||||
self.inputFile = None
|
||||
if isReadable( self.inPoller ):
|
||||
@@ -344,8 +390,12 @@ class CLI( Cmd ):
|
||||
if not node.waiting:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
# There is an at least one race condition here, since
|
||||
# it's possible to interrupt ourselves after we've
|
||||
# read data but before it has been printed.
|
||||
node.sendInt()
|
||||
|
||||
|
||||
# Helper functions
|
||||
|
||||
def isReadable( poller ):
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../examples
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
"""
|
||||
link.py: interface and link abstractions for mininet
|
||||
|
||||
It seems useful to bundle functionality for interfaces into a single
|
||||
class.
|
||||
|
||||
Also it seems useful to enable the possibility of multiple flavors of
|
||||
links, including:
|
||||
|
||||
- simple veth pairs
|
||||
- tunneled links
|
||||
- patchable links (which can be disconnected and reconnected via a patchbay)
|
||||
- link simulators (e.g. wireless)
|
||||
|
||||
Basic division of labor:
|
||||
|
||||
Nodes: know how to execute commands
|
||||
Intfs: know how to configure themselves
|
||||
Links: know how to connect nodes together
|
||||
|
||||
Intf: basic interface object that can configure itself
|
||||
TCIntf: interface with bandwidth limiting and delay via tc
|
||||
|
||||
Link: basic link class for creating veth pairs
|
||||
"""
|
||||
|
||||
from mininet.log import info, error, debug
|
||||
from mininet.util import makeIntfPair, quietRun
|
||||
import re
|
||||
|
||||
class Intf( object ):
|
||||
|
||||
"Basic interface object that can configure itself."
|
||||
|
||||
def __init__( self, name, node=None, port=None, link=None, **params ):
|
||||
"""name: interface name (e.g. h1-eth0)
|
||||
node: owning node (where this intf most likely lives)
|
||||
link: parent link if we're part of a link
|
||||
other arguments are passed to config()"""
|
||||
self.node = node
|
||||
self.name = name
|
||||
self.link = link
|
||||
self.mac, self.ip, self.prefixLen = None, None, None
|
||||
# Add to node (and move ourselves if necessary )
|
||||
node.addIntf( self, port=port )
|
||||
# Save params for future reference
|
||||
self.params = params
|
||||
self.config( **params )
|
||||
|
||||
def cmd( self, *args, **kwargs ):
|
||||
"Run a command in our owning node"
|
||||
return self.node.cmd( *args, **kwargs )
|
||||
|
||||
def ifconfig( self, *args ):
|
||||
"Configure ourselves using ifconfig"
|
||||
return self.cmd( 'ifconfig', self.name, *args )
|
||||
|
||||
def setIP( self, ipstr, prefixLen=None ):
|
||||
"""Set our IP address"""
|
||||
# This is a sign that we should perhaps rethink our prefix
|
||||
# mechanism and/or the way we specify IP addresses
|
||||
if '/' in ipstr:
|
||||
self.ip, self.prefixLen = ipstr.split( '/' )
|
||||
return self.ifconfig( ipstr, 'up' )
|
||||
else:
|
||||
self.ip, self.prefixLen = ipstr, prefixLen
|
||||
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
|
||||
|
||||
def setMAC( self, macstr ):
|
||||
"""Set the MAC address for an interface.
|
||||
macstr: MAC address as string"""
|
||||
self.mac = macstr
|
||||
return ( self.ifconfig( 'down' ) +
|
||||
self.ifconfig( 'hw', 'ether', macstr ) +
|
||||
self.ifconfig( 'up' ) )
|
||||
|
||||
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
|
||||
_macMatchRegex = re.compile( r'..:..:..:..:..:..' )
|
||||
|
||||
def updateIP( self ):
|
||||
"Return updated IP address based on ifconfig"
|
||||
ifconfig = self.ifconfig()
|
||||
ips = self._ipMatchRegex.findall( ifconfig )
|
||||
self.ip = ips[ 0 ] if ips else None
|
||||
return self.ip
|
||||
|
||||
def updateMAC( self ):
|
||||
"Return updated MAC address based on ifconfig"
|
||||
ifconfig = self.ifconfig()
|
||||
macs = self._macMatchRegex.findall( ifconfig )
|
||||
self.mac = macs[ 0 ] if macs else None
|
||||
return self.mac
|
||||
|
||||
def IP( self ):
|
||||
"Return IP address"
|
||||
return self.ip
|
||||
|
||||
def MAC( self ):
|
||||
"Return MAC address"
|
||||
return self.mac
|
||||
|
||||
def isUp( self, setUp=False ):
|
||||
"Return whether interface is up"
|
||||
if setUp:
|
||||
self.ifconfig( 'up' )
|
||||
return "UP" in self.ifconfig()
|
||||
|
||||
def rename( self, newname ):
|
||||
"Rename interface"
|
||||
self.ifconfig( 'down' )
|
||||
result = self.cmd( 'ip link set', self.name, 'name', newname )
|
||||
self.name = newname
|
||||
self.ifconfig( 'up' )
|
||||
return result
|
||||
|
||||
# The reason why we configure things in this way is so
|
||||
# That the parameters can be listed and documented in
|
||||
# the config method.
|
||||
# Dealing with subclasses and superclasses is slightly
|
||||
# annoying, but at least the information is there!
|
||||
|
||||
def setParam( self, results, method, **param ):
|
||||
"""Internal method: configure a *single* parameter
|
||||
results: dict of results to update
|
||||
method: config method name
|
||||
param: arg=value (ignore if value=None)
|
||||
value may also be list or dict"""
|
||||
name, value = param.items()[ 0 ]
|
||||
f = getattr( self, method, None )
|
||||
if not f or value is None:
|
||||
return
|
||||
if type( value ) is list:
|
||||
result = f( *value )
|
||||
elif type( value ) is dict:
|
||||
result = f( **value )
|
||||
else:
|
||||
result = f( value )
|
||||
results[ name ] = result
|
||||
return result
|
||||
|
||||
def config( self, mac=None, ip=None, ifconfig=None,
|
||||
up=True, **_params ):
|
||||
"""Configure Node according to (optional) parameters:
|
||||
mac: MAC address
|
||||
ip: IP address
|
||||
ifconfig: arbitrary interface configuration
|
||||
Subclasses should override this method and call
|
||||
the parent class's config(**params)"""
|
||||
# If we were overriding this method, we would call
|
||||
# the superclass config method here as follows:
|
||||
# r = Parent.config( **params )
|
||||
r = {}
|
||||
self.setParam( r, 'setMAC', mac=mac )
|
||||
self.setParam( r, 'setIP', ip=ip )
|
||||
self.setParam( r, 'isUp', up=up )
|
||||
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
|
||||
self.updateIP()
|
||||
self.updateMAC()
|
||||
return r
|
||||
|
||||
def delete( self ):
|
||||
"Delete interface"
|
||||
self.cmd( 'ip link del ' + self.name )
|
||||
if self.node.inNamespace:
|
||||
# Link may have been dumped into root NS
|
||||
quietRun( 'ip link del ' + self.name )
|
||||
|
||||
def __repr__( self ):
|
||||
return '<%s %s>' % ( self.__class__.__name__, self.name )
|
||||
|
||||
def __str__( self ):
|
||||
return self.name
|
||||
|
||||
|
||||
class TCIntf( Intf ):
|
||||
"""Interface customized by tc (traffic control) utility
|
||||
Allows specification of bandwidth limits (various methods)
|
||||
as well as delay, loss and max queue length"""
|
||||
|
||||
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False ):
|
||||
"Return tc commands to set bandwidth"
|
||||
|
||||
cmds, parent = [], ' root '
|
||||
|
||||
if bw and ( bw < 0 or bw > 1000 ):
|
||||
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
|
||||
|
||||
elif bw is not None:
|
||||
# BL: this seems a bit brittle...
|
||||
if ( speedup > 0 and
|
||||
self.node.name[0:1] == 's' ):
|
||||
bw = speedup
|
||||
# This may not be correct - we should look more closely
|
||||
# at the semantics of burst (and cburst) to make sure we
|
||||
# are specifying the correct sizes. For now I have used
|
||||
# the same settings we had in the mininet-hifi code.
|
||||
if use_hfsc:
|
||||
cmds += [ '%s qdisc add dev %s root handle 5:0 hfsc default 1',
|
||||
'%s class add dev %s parent 5:0 classid 5:1 hfsc sc '
|
||||
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
|
||||
elif use_tbf:
|
||||
if latency_ms is None:
|
||||
latency_ms = 15 * 8 / bw
|
||||
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
|
||||
'rate %fMbit burst 15000 latency %fms' %
|
||||
( bw, latency_ms ) ]
|
||||
else:
|
||||
cmds += [ '%s qdisc add dev %s root handle 5:0 htb default 1',
|
||||
'%s class add dev %s parent 5:0 classid 5:1 htb ' +
|
||||
'rate %fMbit burst 15k' % bw ]
|
||||
parent = ' parent 5:1 '
|
||||
|
||||
# ECN or RED
|
||||
if enable_ecn:
|
||||
cmds += [ '%s qdisc add dev %s' + parent +
|
||||
'handle 6: red limit 1000000 ' +
|
||||
'min 30000 max 35000 avpkt 1500 ' +
|
||||
'burst 20 ' +
|
||||
'bandwidth %fmbit probability 1 ecn' % bw ]
|
||||
parent = ' parent 6: '
|
||||
elif enable_red:
|
||||
cmds += [ '%s qdisc add dev %s' + parent +
|
||||
'handle 6: red limit 1000000 ' +
|
||||
'min 30000 max 35000 avpkt 1500 ' +
|
||||
'burst 20 ' +
|
||||
'bandwidth %fmbit probability 1' % bw ]
|
||||
parent = ' parent 6: '
|
||||
return cmds, parent
|
||||
|
||||
@staticmethod
|
||||
def delayCmds( parent, delay=None, jitter=None,
|
||||
loss=None, max_queue_size=None ):
|
||||
"Internal method: return tc commands for delay and loss"
|
||||
cmds = []
|
||||
if delay and delay < 0:
|
||||
error( 'Negative delay', delay, '\n' )
|
||||
elif jitter and jitter < 0:
|
||||
error( 'Negative jitter', jitter, '\n' )
|
||||
elif loss and ( loss < 0 or loss > 100 ):
|
||||
error( 'Bad loss percentage', loss, '%%\n' )
|
||||
else:
|
||||
# Delay/jitter/loss/max queue size
|
||||
netemargs = '%s%s%s%s' % (
|
||||
'delay %s ' % delay if delay is not None else '',
|
||||
'%s ' % jitter if jitter is not None else '',
|
||||
'loss %d ' % loss if loss is not None else '',
|
||||
'limit %d' % max_queue_size if max_queue_size is not None
|
||||
else '' )
|
||||
if netemargs:
|
||||
cmds = [ '%s qdisc add dev %s ' + parent +
|
||||
' handle 10: netem ' +
|
||||
netemargs ]
|
||||
parent = ' parent 10:1 '
|
||||
return cmds, parent
|
||||
|
||||
def tc( self, cmd, tc='tc' ):
|
||||
"Execute tc command for our interface"
|
||||
c = cmd % (tc, self) # Add in tc command and our name
|
||||
debug(" *** executing command: %s\n" % c)
|
||||
return self.cmd( c )
|
||||
|
||||
def config( self, bw=None, delay=None, jitter=None, loss=None,
|
||||
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False,
|
||||
max_queue_size=None, **params ):
|
||||
"Configure the port and set its properties."
|
||||
|
||||
result = Intf.config( self, **params)
|
||||
|
||||
# Disable GRO
|
||||
if disable_gro:
|
||||
self.cmd( 'ethtool -K %s gro off' % self )
|
||||
|
||||
# Optimization: return if nothing else to configure
|
||||
# Question: what happens if we want to reset things?
|
||||
if ( bw is None and not delay and not loss
|
||||
and max_queue_size is None ):
|
||||
return
|
||||
|
||||
# Clear existing configuration
|
||||
tcoutput = self.tc( '%s qdisc show dev %s' )
|
||||
if "priomap" not in tcoutput:
|
||||
cmds = [ '%s qdisc del dev %s root' ]
|
||||
else:
|
||||
cmds = []
|
||||
|
||||
# Bandwidth limits via various methods
|
||||
bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
|
||||
use_hfsc=use_hfsc, use_tbf=use_tbf,
|
||||
latency_ms=latency_ms,
|
||||
enable_ecn=enable_ecn,
|
||||
enable_red=enable_red )
|
||||
cmds += bwcmds
|
||||
|
||||
# Delay/jitter/loss/max_queue_size using netem
|
||||
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter,
|
||||
loss=loss, max_queue_size=max_queue_size,
|
||||
parent=parent )
|
||||
cmds += delaycmds
|
||||
|
||||
# Ugly but functional: display configuration info
|
||||
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
|
||||
( [ '%s delay' % delay ] if delay is not None else [] ) +
|
||||
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
|
||||
( ['%d%% loss' % loss ] if loss is not None else [] ) +
|
||||
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
|
||||
if enable_red else [] ) )
|
||||
info( '(' + ' '.join( stuff ) + ') ' )
|
||||
|
||||
# Execute all the commands in our node
|
||||
debug("at map stage w/cmds: %s\n" % cmds)
|
||||
tcoutputs = [ self.tc(cmd) for cmd in cmds ]
|
||||
for output in tcoutputs:
|
||||
if output != '':
|
||||
error( "*** Error: %s" % output )
|
||||
debug( "cmds:", cmds, '\n' )
|
||||
debug( "outputs:", tcoutputs, '\n' )
|
||||
result[ 'tcoutputs'] = tcoutputs
|
||||
result[ 'parent' ] = parent
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Link( object ):
|
||||
|
||||
"""A basic link is just a veth pair.
|
||||
Other types of links could be tunnels, link emulators, etc.."""
|
||||
|
||||
def __init__( self, node1, node2, port1=None, port2=None,
|
||||
intfName1=None, intfName2=None,
|
||||
intf=Intf, cls1=None, cls2=None, params1=None,
|
||||
params2=None ):
|
||||
"""Create veth link to another node, making two new interfaces.
|
||||
node1: first node
|
||||
node2: second node
|
||||
port1: node1 port number (optional)
|
||||
port2: node2 port number (optional)
|
||||
intf: default interface class/constructor
|
||||
cls1, cls2: optional interface-specific constructors
|
||||
intfName1: node1 interface name (optional)
|
||||
intfName2: node2 interface name (optional)
|
||||
params1: parameters for interface 1
|
||||
params2: parameters for interface 2"""
|
||||
# This is a bit awkward; it seems that having everything in
|
||||
# params would be more orthogonal, but being able to specify
|
||||
# in-line arguments is more convenient!
|
||||
if port1 is None:
|
||||
port1 = node1.newPort()
|
||||
if port2 is None:
|
||||
port2 = node2.newPort()
|
||||
if not intfName1:
|
||||
intfName1 = self.intfName( node1, port1 )
|
||||
if not intfName2:
|
||||
intfName2 = self.intfName( node2, port2 )
|
||||
|
||||
self.makeIntfPair( intfName1, intfName2 )
|
||||
|
||||
if not cls1:
|
||||
cls1 = intf
|
||||
if not cls2:
|
||||
cls2 = intf
|
||||
if not params1:
|
||||
params1 = {}
|
||||
if not params2:
|
||||
params2 = {}
|
||||
|
||||
intf1 = cls1( name=intfName1, node=node1, port=port1,
|
||||
link=self, **params1 )
|
||||
intf2 = cls2( name=intfName2, node=node2, port=port2,
|
||||
link=self, **params2 )
|
||||
|
||||
# All we are is dust in the wind, and our two interfaces
|
||||
self.intf1, self.intf2 = intf1, intf2
|
||||
|
||||
@classmethod
|
||||
def intfName( cls, node, n ):
|
||||
"Construct a canonical interface name node-ethN for interface n."
|
||||
return node.name + '-eth' + repr( n )
|
||||
|
||||
@classmethod
|
||||
def makeIntfPair( cls, intf1, intf2 ):
|
||||
"""Create pair of interfaces
|
||||
intf1: name of interface 1
|
||||
intf2: name of interface 2
|
||||
(override this class method [and possibly delete()]
|
||||
to change link type)"""
|
||||
makeIntfPair( intf1, intf2 )
|
||||
|
||||
def delete( self ):
|
||||
"Delete this link"
|
||||
self.intf1.delete()
|
||||
self.intf2.delete()
|
||||
|
||||
def __str__( self ):
|
||||
return '%s<->%s' % ( self.intf1, self.intf2 )
|
||||
|
||||
class TCLink( Link ):
|
||||
"Link with symmetric TC interfaces configured via opts"
|
||||
def __init__( self, node1, node2, port1=None, port2=None,
|
||||
intfName1=None, intfName2=None, **params ):
|
||||
Link.__init__( self, node1, node2, port1=port1, port2=port2,
|
||||
intfName1=intfName1, intfName2=intfName2,
|
||||
cls1=TCIntf,
|
||||
cls2=TCIntf,
|
||||
params1=params,
|
||||
params2=params)
|
||||
+15
-17
@@ -11,11 +11,11 @@ import types
|
||||
OUTPUT = 25
|
||||
|
||||
LEVELS = { 'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
|
||||
# change this to logging.INFO to get printouts when running unit tests
|
||||
LOGLEVELDEFAULT = OUTPUT
|
||||
@@ -57,21 +57,19 @@ class StreamHandlerNoNewline( logging.StreamHandler ):
|
||||
|
||||
class Singleton( type ):
|
||||
"""Singleton pattern from Wikipedia
|
||||
See http://en.wikipedia.org/wiki/SingletonPattern#Python
|
||||
See http://en.wikipedia.org/wiki/Singleton_Pattern
|
||||
|
||||
Intended to be used as a __metaclass_ param, as shown for the class
|
||||
below.
|
||||
below."""
|
||||
|
||||
Changed cls first args to mcs to satisfy pylint."""
|
||||
def __init__( cls, name, bases, dict_ ):
|
||||
super( Singleton, cls ).__init__( name, bases, dict_ )
|
||||
cls.instance = None
|
||||
|
||||
def __init__( mcs, name, bases, dict_ ):
|
||||
super( Singleton, mcs ).__init__( name, bases, dict_ )
|
||||
mcs.instance = None
|
||||
|
||||
def __call__( mcs, *args, **kw ):
|
||||
if mcs.instance is None:
|
||||
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
|
||||
return mcs.instance
|
||||
def __call__( cls, *args, **kw ):
|
||||
if cls.instance is None:
|
||||
cls.instance = super( Singleton, cls ).__call__( *args, **kw )
|
||||
return cls.instance
|
||||
|
||||
|
||||
class MininetLogger( Logger, object ):
|
||||
@@ -117,7 +115,7 @@ class MininetLogger( Logger, object ):
|
||||
Convenience function to support lowercase names.
|
||||
levelName: level name from LEVELS"""
|
||||
level = LOGLEVELDEFAULT
|
||||
if levelname != None:
|
||||
if levelname is not None:
|
||||
if levelname not in LEVELS:
|
||||
raise Exception( 'unknown levelname seen in setLogLevel' )
|
||||
else:
|
||||
|
||||
@@ -19,7 +19,7 @@ def modprobe( mod ):
|
||||
return quietRun( [ 'modprobe', mod ] )
|
||||
|
||||
OF_KMOD = 'ofdatapath'
|
||||
OVS_KMOD = 'openvswitch_mod'
|
||||
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
|
||||
TUN = 'tun'
|
||||
|
||||
def moduleDeps( subtract=None, add=None ):
|
||||
@@ -48,8 +48,8 @@ def moduleDeps( subtract=None, add=None ):
|
||||
modprobeOutput = modprobe( mod )
|
||||
if modprobeOutput:
|
||||
error( 'Error inserting ' + mod +
|
||||
' - is it installed and available via modprobe?\n' +
|
||||
'Error was: "%s"\n' % modprobeOutput )
|
||||
' - is it installed and available via modprobe?\n' +
|
||||
'Error was: "%s"\n' % modprobeOutput )
|
||||
if mod not in lsmod():
|
||||
error( 'Failed to insert ' + mod + ' - quitting.\n' )
|
||||
exit( 1 )
|
||||
@@ -63,6 +63,6 @@ def pathCheck( *args, **kwargs ):
|
||||
for arg in args:
|
||||
if not quietRun( 'which ' + arg ):
|
||||
error( 'Cannot find required executable %s.\n' % arg +
|
||||
'Please make sure that %s is installed ' % moduleName +
|
||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||
'Please make sure that %s is installed ' % moduleName +
|
||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||
exit( 1 )
|
||||
|
||||
+476
-185
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
|
||||
Mininet: A simple networking testbed for OpenFlow!
|
||||
Mininet: A simple networking testbed for OpenFlow/SDN!
|
||||
|
||||
author: Bob Lantz (rlantz@cs.stanford.edu)
|
||||
author: Brandon Heller (brandonh@stanford.edu)
|
||||
@@ -90,235 +90,338 @@ import os
|
||||
import re
|
||||
import select
|
||||
import signal
|
||||
import copy
|
||||
from time import sleep
|
||||
from itertools import chain, groupby
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import info, error, debug, output
|
||||
from mininet.node import Host, UserSwitch, OVSKernelSwitch, Controller
|
||||
from mininet.node import ControllerParams
|
||||
from mininet.util import quietRun, fixLimits
|
||||
from mininet.util import createLink, macColonHex, ipStr, ipParse
|
||||
from mininet.log import info, error, debug, output, warn
|
||||
from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller
|
||||
from mininet.nodelib import NAT
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
|
||||
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
|
||||
from mininet.term import cleanUpScreens, makeTerms
|
||||
|
||||
# Mininet version: should be consistent with README and LICENSE
|
||||
VERSION = "2.1.0+"
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
|
||||
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
|
||||
controller=Controller,
|
||||
cparams=ControllerParams( '10.0.0.0', 8 ),
|
||||
build=True, xterms=False, cleanup=False,
|
||||
inNamespace=False,
|
||||
autoSetMacs=False, autoStaticArp=False, listenPort=None ):
|
||||
controller=DefaultController, link=Link, intf=Intf,
|
||||
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||
inNamespace=False,
|
||||
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
|
||||
listenPort=None, waitConnected=False ):
|
||||
"""Create Mininet object.
|
||||
topo: Topo (topology) object or None
|
||||
switch: Switch class
|
||||
host: Host class
|
||||
controller: Controller class
|
||||
cparams: ControllerParams object
|
||||
switch: default Switch class
|
||||
host: default Host class/constructor
|
||||
controller: default Controller class/constructor
|
||||
link: default Link class/constructor
|
||||
intf: default Intf class/constructor
|
||||
ipBase: base IP address for hosts,
|
||||
build: build now from topo?
|
||||
xterms: if build now, spawn xterms?
|
||||
cleanup: if build now, cleanup before creating?
|
||||
inNamespace: spawn switches and controller in net namespaces?
|
||||
autoSetMacs: set MAC addrs from topo?
|
||||
autoSetMacs: set MAC addrs automatically like IP addresses?
|
||||
autoStaticArp: set all-pairs static MAC addrs?
|
||||
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
|
||||
listenPort: base listening port to open; will be incremented for
|
||||
each additional switch in the net if inNamespace=False"""
|
||||
self.topo = topo
|
||||
self.switch = switch
|
||||
self.host = host
|
||||
self.controller = controller
|
||||
self.cparams = cparams
|
||||
self.topo = topo
|
||||
self.link = link
|
||||
self.intf = intf
|
||||
self.ipBase = ipBase
|
||||
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
|
||||
self.nextIP = 1 # start for address allocation
|
||||
self.inNamespace = inNamespace
|
||||
self.xterms = xterms
|
||||
self.cleanup = cleanup
|
||||
self.autoSetMacs = autoSetMacs
|
||||
self.autoStaticArp = autoStaticArp
|
||||
self.autoPinCpus = autoPinCpus
|
||||
self.numCores = numCores()
|
||||
self.nextCore = 0 # next core for pinning hosts to CPUs
|
||||
self.listenPort = listenPort
|
||||
self.waitConn = waitConnected
|
||||
|
||||
self.hosts = []
|
||||
self.switches = []
|
||||
self.controllers = []
|
||||
|
||||
self.nameToNode = {} # name to Node (Host/Switch) objects
|
||||
self.idToNode = {} # dpid to Node (Host/Switch) objects
|
||||
self.dps = 0 # number of created kernel datapaths
|
||||
|
||||
self.terms = [] # list of spawned xterm processes
|
||||
|
||||
init()
|
||||
switch.setup()
|
||||
Mininet.init() # Initialize Mininet if necessary
|
||||
|
||||
self.built = False
|
||||
if topo and build:
|
||||
self.build()
|
||||
|
||||
def addHost( self, name, mac=None, ip=None ):
|
||||
|
||||
def waitConnected( self, timeout=None, delay=.5 ):
|
||||
"""wait for each switch to connect to a controller,
|
||||
up to 5 seconds
|
||||
timeout: time to wait, or None to wait indefinitely
|
||||
delay: seconds to sleep per iteration
|
||||
returns: True if all switches are connected"""
|
||||
info( '*** Waiting for switches to connect\n' )
|
||||
time = 0
|
||||
remaining = list( self.switches )
|
||||
while True:
|
||||
for switch in tuple( remaining ):
|
||||
if switch.connected():
|
||||
info( '%s ' % switch )
|
||||
remaining.remove( switch )
|
||||
if not remaining:
|
||||
info( '\n' )
|
||||
return True
|
||||
if time > timeout and timeout is not None:
|
||||
break
|
||||
sleep( delay )
|
||||
time += delay
|
||||
warn( 'Timed out after %d seconds\n' % time )
|
||||
for switch in remaining:
|
||||
if not switch.connected():
|
||||
warn( 'Warning: %s is not connected to a controller\n'
|
||||
% switch.name )
|
||||
else:
|
||||
remaining.remove( switch )
|
||||
return not remaining
|
||||
|
||||
def addHost( self, name, cls=None, **params ):
|
||||
"""Add host.
|
||||
name: name of host to add
|
||||
mac: default MAC address for intf 0
|
||||
ip: default IP address for intf 0
|
||||
cls: custom host class/constructor (optional)
|
||||
params: parameters for host
|
||||
returns: added host"""
|
||||
host = self.host( name, defaultMAC=mac, defaultIP=ip )
|
||||
self.hosts.append( host )
|
||||
self.nameToNode[ name ] = host
|
||||
return host
|
||||
# Default IP and MAC addresses
|
||||
defaults = { 'ip': ipAdd( self.nextIP,
|
||||
ipBaseNum=self.ipBaseNum,
|
||||
prefixLen=self.prefixLen ) +
|
||||
'/%s' % self.prefixLen }
|
||||
if self.autoSetMacs:
|
||||
defaults[ 'mac' ] = macColonHex( self.nextIP )
|
||||
if self.autoPinCpus:
|
||||
defaults[ 'cores' ] = self.nextCore
|
||||
self.nextCore = ( self.nextCore + 1 ) % self.numCores
|
||||
self.nextIP += 1
|
||||
defaults.update( params )
|
||||
if not cls:
|
||||
cls = self.host
|
||||
h = cls( name, **defaults )
|
||||
self.hosts.append( h )
|
||||
self.nameToNode[ name ] = h
|
||||
return h
|
||||
|
||||
def addSwitch( self, name, mac=None, ip=None ):
|
||||
def addSwitch( self, name, cls=None, **params ):
|
||||
"""Add switch.
|
||||
name: name of switch to add
|
||||
mac: default MAC address for kernel/OVS switch intf 0
|
||||
cls: custom switch class/constructor (optional)
|
||||
returns: added switch
|
||||
side effect: increments the listenPort member variable."""
|
||||
if self.switch == UserSwitch:
|
||||
sw = self.switch( name, listenPort=self.listenPort,
|
||||
defaultMAC=mac, defaultIP=ip, inNamespace=self.inNamespace )
|
||||
else:
|
||||
sw = self.switch( name, listenPort=self.listenPort,
|
||||
defaultMAC=mac, defaultIP=ip, dp=self.dps,
|
||||
inNamespace=self.inNamespace )
|
||||
side effect: increments listenPort ivar ."""
|
||||
defaults = { 'listenPort': self.listenPort,
|
||||
'inNamespace': self.inNamespace }
|
||||
defaults.update( params )
|
||||
if not cls:
|
||||
cls = self.switch
|
||||
sw = cls( name, **defaults )
|
||||
if not self.inNamespace and self.listenPort:
|
||||
self.listenPort += 1
|
||||
self.dps += 1
|
||||
self.switches.append( sw )
|
||||
self.nameToNode[ name ] = sw
|
||||
return sw
|
||||
|
||||
def addController( self, name='c0', controller=None, **kwargs ):
|
||||
def addController( self, name='c0', controller=None, **params ):
|
||||
"""Add controller.
|
||||
controller: Controller class"""
|
||||
# Get controller class
|
||||
if not controller:
|
||||
controller = self.controller
|
||||
controller_new = controller( name, **kwargs )
|
||||
if controller_new: # allow controller-less setups
|
||||
# Construct new controller if one is not given
|
||||
if isinstance( name, Controller ):
|
||||
controller_new = name
|
||||
# Pylint thinks controller is a str()
|
||||
# pylint: disable=E1103
|
||||
name = controller_new.name
|
||||
# pylint: enable=E1103
|
||||
else:
|
||||
controller_new = controller( name, **params )
|
||||
# Add new controller to net
|
||||
if controller_new: # allow controller-less setups
|
||||
self.controllers.append( controller_new )
|
||||
self.nameToNode[ name ] = controller_new
|
||||
return controller_new
|
||||
|
||||
# Control network support:
|
||||
#
|
||||
# Create an explicit control network. Currently this is only
|
||||
# used by the user datapath configuration.
|
||||
#
|
||||
# Notes:
|
||||
#
|
||||
# 1. If the controller and switches are in the same (e.g. root)
|
||||
# namespace, they can just use the loopback connection.
|
||||
#
|
||||
# 2. If we can get unix domain sockets to work, we can use them
|
||||
# instead of an explicit control network.
|
||||
#
|
||||
# 3. Instead of routing, we could bridge or use 'in-band' control.
|
||||
#
|
||||
# 4. Even if we dispense with this in general, it could still be
|
||||
# useful for people who wish to simulate a separate control
|
||||
# network (since real networks may need one!)
|
||||
def addNAT( self, name='nat0', connect=True, inNamespace=False, **params ):
|
||||
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
|
||||
subnet=self.ipBase, **params )
|
||||
# find first switch and create link
|
||||
if connect:
|
||||
# connect the nat to the first switch
|
||||
self.addLink( nat, self.switches[ 0 ] )
|
||||
# set the default route on hosts
|
||||
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
|
||||
for host in self.hosts:
|
||||
if host.inNamespace:
|
||||
host.setDefaultRoute( 'via %s' % natIP )
|
||||
return nat
|
||||
|
||||
def configureControlNetwork( self ):
|
||||
"Configure control network."
|
||||
self.configureRoutedControlNetwork()
|
||||
# BL: We now have four ways to look up nodes
|
||||
# This may (should?) be cleaned up in the future.
|
||||
def getNodeByName( self, *args ):
|
||||
"Return node(s) with given name(s)"
|
||||
if len( args ) == 1:
|
||||
return self.nameToNode[ args[ 0 ] ]
|
||||
return [ self.nameToNode[ n ] for n in args ]
|
||||
|
||||
# We still need to figure out the right way to pass
|
||||
# in the control network location.
|
||||
def get( self, *args ):
|
||||
"Convenience alias for getNodeByName"
|
||||
return self.getNodeByName( *args )
|
||||
|
||||
def configureRoutedControlNetwork( self, ip='192.168.123.1',
|
||||
prefixLen=16 ):
|
||||
"""Configure a routed control network on controller and switches.
|
||||
For use with the user datapath only right now.
|
||||
"""
|
||||
controller = self.controllers[ 0 ]
|
||||
info( controller.name + ' <->' )
|
||||
cip = ip
|
||||
snum = ipParse( ip )
|
||||
for switch in self.switches:
|
||||
info( ' ' + switch.name )
|
||||
sintf, cintf = createLink( switch, controller )
|
||||
snum += 1
|
||||
while snum & 0xff in [ 0, 255 ]:
|
||||
snum += 1
|
||||
sip = ipStr( snum )
|
||||
controller.setIP( cintf, cip, prefixLen )
|
||||
switch.setIP( sintf, sip, prefixLen )
|
||||
controller.setHostRoute( sip, cintf )
|
||||
switch.setHostRoute( cip, sintf )
|
||||
info( '\n' )
|
||||
info( '*** Testing control network\n' )
|
||||
while not controller.intfIsUp( cintf ):
|
||||
info( '*** Waiting for', cintf, 'to come up\n' )
|
||||
sleep( 1 )
|
||||
for switch in self.switches:
|
||||
while not switch.intfIsUp( sintf ):
|
||||
info( '*** Waiting for', sintf, 'to come up\n' )
|
||||
sleep( 1 )
|
||||
if self.ping( hosts=[ switch, controller ] ) != 0:
|
||||
error( '*** Error: control network test failed\n' )
|
||||
exit( 1 )
|
||||
info( '\n' )
|
||||
# Even more convenient syntax for node lookup and iteration
|
||||
def __getitem__( self, key ):
|
||||
"""net [ name ] operator: Return node(s) with given name(s)"""
|
||||
return self.nameToNode[ key ]
|
||||
|
||||
def __iter__( self ):
|
||||
"return iterator over node names"
|
||||
for node in chain( self.hosts, self.switches, self.controllers ):
|
||||
yield node.name
|
||||
|
||||
def __len__( self ):
|
||||
"returns number of nodes in net"
|
||||
return ( len( self.hosts ) + len( self.switches ) +
|
||||
len( self.controllers ) )
|
||||
|
||||
def __contains__( self, item ):
|
||||
"returns True if net contains named node"
|
||||
return item in self.nameToNode
|
||||
|
||||
def keys( self ):
|
||||
"return a list of all node names or net's keys"
|
||||
return list( self )
|
||||
|
||||
def values( self ):
|
||||
"return a list of all nodes or net's values"
|
||||
return [ self[name] for name in self ]
|
||||
|
||||
def items( self ):
|
||||
"return (key,value) tuple list for every node in net"
|
||||
return zip( self.keys(), self.values() )
|
||||
|
||||
def addLink( self, node1, node2, port1=None, port2=None,
|
||||
cls=None, **params ):
|
||||
""""Add a link from node1 to node2
|
||||
node1: source node
|
||||
node2: dest node
|
||||
port1: source port
|
||||
port2: dest port
|
||||
returns: link object"""
|
||||
defaults = { 'port1': port1,
|
||||
'port2': port2,
|
||||
'intf': self.intf }
|
||||
defaults.update( params )
|
||||
if not cls:
|
||||
cls = self.link
|
||||
return cls( node1, node2, **defaults )
|
||||
|
||||
def configHosts( self ):
|
||||
"Configure a set of hosts."
|
||||
# params were: hosts, ips
|
||||
for host in self.hosts:
|
||||
hintf = host.intfs[ 0 ]
|
||||
host.setIP( hintf, host.defaultIP, self.cparams.prefixLen )
|
||||
host.setDefaultRoute( hintf )
|
||||
# You're low priority, dude!
|
||||
quietRun( 'renice +18 -p ' + repr( host.pid ) )
|
||||
info( host.name + ' ' )
|
||||
intf = host.defaultIntf()
|
||||
if intf:
|
||||
host.configDefault()
|
||||
else:
|
||||
# Don't configure nonexistent intf
|
||||
host.configDefault( ip=None, mac=None )
|
||||
# You're low priority, dude!
|
||||
# BL: do we want to do this here or not?
|
||||
# May not make sense if we have CPU lmiting...
|
||||
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
|
||||
# This may not be the right place to do this, but
|
||||
# it needs to be done somewhere.
|
||||
host.cmd( 'ifconfig lo up' )
|
||||
info( '\n' )
|
||||
|
||||
def buildFromTopo( self, topo ):
|
||||
def buildFromTopo( self, topo=None ):
|
||||
"""Build mininet from a topology object
|
||||
At the end of this function, everything should be connected
|
||||
and up."""
|
||||
|
||||
def addNode( prefix, addMethod, nodeId ):
|
||||
"Add a host or a switch."
|
||||
name = prefix + topo.name( nodeId )
|
||||
mac = macColonHex( nodeId ) if self.setMacs else None
|
||||
ip = topo.ip( nodeId )
|
||||
node = addMethod( name, mac=mac, ip=ip )
|
||||
self.idToNode[ nodeId ] = node
|
||||
info( name + ' ' )
|
||||
|
||||
# Possibly we should clean up here and/or validate
|
||||
# the topo
|
||||
if self.cleanup:
|
||||
pass
|
||||
|
||||
info( '*** Adding controller\n' )
|
||||
self.addController( 'c0' )
|
||||
info( '*** Creating network\n' )
|
||||
|
||||
if not self.controllers and self.controller:
|
||||
# Add a default controller
|
||||
info( '*** Adding controller\n' )
|
||||
classes = self.controller
|
||||
if type( classes ) is not list:
|
||||
classes = [ classes ]
|
||||
for i, cls in enumerate( classes ):
|
||||
# Allow Controller objects because nobody understands currying
|
||||
if isinstance( cls, Controller ):
|
||||
self.addController( cls )
|
||||
else:
|
||||
self.addController( 'c%d' % i, cls )
|
||||
|
||||
info( '*** Adding hosts:\n' )
|
||||
for hostId in sorted( topo.hosts() ):
|
||||
addNode( 'h', self.addHost, hostId )
|
||||
for hostName in topo.hosts():
|
||||
self.addHost( hostName, **topo.nodeInfo( hostName ) )
|
||||
info( hostName + ' ' )
|
||||
|
||||
info( '\n*** Adding switches:\n' )
|
||||
for switchId in sorted( topo.switches() ):
|
||||
addNode( 's', self.addSwitch, switchId )
|
||||
for switchName in topo.switches():
|
||||
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
|
||||
info( switchName + ' ' )
|
||||
|
||||
info( '\n*** Adding links:\n' )
|
||||
for srcId, dstId in sorted( topo.edges() ):
|
||||
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
|
||||
srcPort, dstPort = topo.port( srcId, dstId )
|
||||
createLink( src, dst, srcPort, dstPort )
|
||||
for srcName, dstName in topo.links(sort=True):
|
||||
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
|
||||
params = topo.linkInfo( srcName, dstName )
|
||||
srcPort, dstPort = topo.port( srcName, dstName )
|
||||
self.addLink( src, dst, srcPort, dstPort, **params )
|
||||
info( '(%s, %s) ' % ( src.name, dst.name ) )
|
||||
|
||||
info( '\n' )
|
||||
|
||||
def configureControlNetwork( self ):
|
||||
"Control net config hook: override in subclass"
|
||||
raise Exception( 'configureControlNetwork: '
|
||||
'should be overriden in subclass', self )
|
||||
|
||||
def build( self ):
|
||||
"Build mininet."
|
||||
if self.topo:
|
||||
self.buildFromTopo( self.topo )
|
||||
if self.inNamespace:
|
||||
info( '*** Configuring control network\n' )
|
||||
self.configureControlNetwork()
|
||||
info( '*** Configuring hosts\n' )
|
||||
self.configHosts()
|
||||
if self.xterms:
|
||||
self.startTerms()
|
||||
if self.autoSetMacs:
|
||||
self.setMacs()
|
||||
if self.autoStaticArp:
|
||||
self.staticArp()
|
||||
self.built = True
|
||||
|
||||
def startTerms( self ):
|
||||
"Start a terminal for each node."
|
||||
if 'DISPLAY' not in os.environ:
|
||||
error( "Error starting terms: Cannot connect to display\n" )
|
||||
return
|
||||
info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
|
||||
cleanUpScreens()
|
||||
self.terms += makeTerms( self.controllers, 'controller' )
|
||||
@@ -327,17 +430,10 @@ class Mininet( object ):
|
||||
|
||||
def stopXterms( self ):
|
||||
"Kill each xterm."
|
||||
# Kill xterms
|
||||
for term in self.terms:
|
||||
os.kill( term.pid, signal.SIGKILL )
|
||||
cleanUpScreens()
|
||||
|
||||
def setMacs( self ):
|
||||
"""Set MAC addrs to correspond to default MACs on hosts.
|
||||
Assume that the host only has one interface."""
|
||||
for host in self.hosts:
|
||||
host.setMAC( host.intfs[ 0 ], host.defaultMAC )
|
||||
|
||||
def staticArp( self ):
|
||||
"Add all-pairs ARP entries to remove the need to handle broadcast."
|
||||
for src in self.hosts:
|
||||
@@ -357,26 +453,32 @@ class Mininet( object ):
|
||||
info( switch.name + ' ')
|
||||
switch.start( self.controllers )
|
||||
info( '\n' )
|
||||
if self.waitConn:
|
||||
self.waitConnected()
|
||||
|
||||
def stop( self ):
|
||||
"Stop the controller(s), switches and hosts"
|
||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||
for controller in self.controllers:
|
||||
info( controller.name + ' ' )
|
||||
controller.stop()
|
||||
info( '\n' )
|
||||
if self.terms:
|
||||
info( '*** Stopping %i terms\n' % len( self.terms ) )
|
||||
self.stopXterms()
|
||||
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
||||
for host in self.hosts:
|
||||
info( '%s ' % host.name )
|
||||
host.terminate()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
for swclass, switches in groupby( sorted( self.switches, key=type ), type ):
|
||||
if hasattr( swclass, 'batchShutdown' ):
|
||||
swclass.batchShutdown( switches )
|
||||
for switch in self.switches:
|
||||
info( switch.name )
|
||||
info( switch.name + ' ' )
|
||||
switch.stop()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||
for controller in self.controllers:
|
||||
controller.stop()
|
||||
info( '*** Done\n' )
|
||||
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
||||
for host in self.hosts:
|
||||
info( host.name + ' ' )
|
||||
host.terminate()
|
||||
info( '\n*** Done\n' )
|
||||
|
||||
def run( self, test, *args, **kwargs ):
|
||||
"Perform a complete start/test/stop cycle."
|
||||
@@ -410,24 +512,28 @@ class Mininet( object ):
|
||||
if not ready and timeoutms >= 0:
|
||||
yield None, None
|
||||
|
||||
# XXX These test methods should be moved out of this class.
|
||||
# Probably we should create a tests.py for them
|
||||
|
||||
@staticmethod
|
||||
def _parsePing( pingOutput ):
|
||||
"Parse ping output and return packets sent, received."
|
||||
# Check for downed link
|
||||
if 'connect: Network is unreachable' in pingOutput:
|
||||
return (1, 0)
|
||||
return 1, 0
|
||||
r = r'(\d+) packets transmitted, (\d+) received'
|
||||
m = re.search( r, pingOutput )
|
||||
if m == None:
|
||||
if m is None:
|
||||
error( '*** Error: could not parse ping output: %s\n' %
|
||||
pingOutput )
|
||||
return (1, 0)
|
||||
pingOutput )
|
||||
return 1, 0
|
||||
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
|
||||
return sent, received
|
||||
|
||||
def ping( self, hosts=None ):
|
||||
def ping( self, hosts=None, timeout=None ):
|
||||
"""Ping between all specified hosts.
|
||||
hosts: list of hosts
|
||||
timeout: time to wait for a response, as string
|
||||
returns: ploss packet loss percentage"""
|
||||
# should we check if running?
|
||||
packets = 0
|
||||
@@ -440,7 +546,10 @@ class Mininet( object ):
|
||||
output( '%s -> ' % node.name )
|
||||
for dest in hosts:
|
||||
if node != dest:
|
||||
result = node.cmd( 'ping -c1 ' + dest.IP() )
|
||||
opts = ''
|
||||
if timeout:
|
||||
opts = '-W %s' % timeout
|
||||
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
|
||||
sent, received = self._parsePing( result )
|
||||
packets += sent
|
||||
if received > sent:
|
||||
@@ -451,15 +560,82 @@ class Mininet( object ):
|
||||
lost += sent - received
|
||||
output( ( '%s ' % dest.name ) if received else 'X ' )
|
||||
output( '\n' )
|
||||
ploss = 100 * lost / packets
|
||||
output( "*** Results: %i%% dropped (%d/%d lost)\n" %
|
||||
( ploss, lost, packets ) )
|
||||
if packets > 0:
|
||||
ploss = 100.0 * lost / packets
|
||||
received = packets - lost
|
||||
output( "*** Results: %i%% dropped (%d/%d received)\n" %
|
||||
( ploss, received, packets ) )
|
||||
else:
|
||||
ploss = 0
|
||||
output( "*** Warning: No packets sent\n" )
|
||||
return ploss
|
||||
|
||||
def pingAll( self ):
|
||||
@staticmethod
|
||||
def _parsePingFull( pingOutput ):
|
||||
"Parse ping output and return all data."
|
||||
errorTuple = (1, 0, 0, 0, 0, 0)
|
||||
# Check for downed link
|
||||
r = r'[uU]nreachable'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is not None:
|
||||
return errorTuple
|
||||
r = r'(\d+) packets transmitted, (\d+) received'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is None:
|
||||
error( '*** Error: could not parse ping output: %s\n' %
|
||||
pingOutput )
|
||||
return errorTuple
|
||||
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
|
||||
r = r'rtt min/avg/max/mdev = '
|
||||
r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is None:
|
||||
error( '*** Error: could not parse ping output: %s\n' %
|
||||
pingOutput )
|
||||
return errorTuple
|
||||
rttmin = float( m.group( 1 ) )
|
||||
rttavg = float( m.group( 2 ) )
|
||||
rttmax = float( m.group( 3 ) )
|
||||
rttdev = float( m.group( 4 ) )
|
||||
return sent, received, rttmin, rttavg, rttmax, rttdev
|
||||
|
||||
def pingFull( self, hosts=None, timeout=None ):
|
||||
"""Ping between all specified hosts and return all data.
|
||||
hosts: list of hosts
|
||||
timeout: time to wait for a response, as string
|
||||
returns: all ping data; see function body."""
|
||||
# should we check if running?
|
||||
# Each value is a tuple: (src, dsd, [all ping outputs])
|
||||
all_outputs = []
|
||||
if not hosts:
|
||||
hosts = self.hosts
|
||||
output( '*** Ping: testing ping reachability\n' )
|
||||
for node in hosts:
|
||||
output( '%s -> ' % node.name )
|
||||
for dest in hosts:
|
||||
if node != dest:
|
||||
opts = ''
|
||||
if timeout:
|
||||
opts = '-W %s' % timeout
|
||||
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
|
||||
outputs = self._parsePingFull( result )
|
||||
sent, received, rttmin, rttavg, rttmax, rttdev = outputs
|
||||
all_outputs.append( (node, dest, outputs) )
|
||||
output( ( '%s ' % dest.name ) if received else 'X ' )
|
||||
output( '\n' )
|
||||
output( "*** Results: \n" )
|
||||
for outputs in all_outputs:
|
||||
src, dest, ping_outputs = outputs
|
||||
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
|
||||
output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
|
||||
output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
|
||||
(rttmin, rttavg, rttmax, rttdev) )
|
||||
return all_outputs
|
||||
|
||||
def pingAll( self, timeout=None ):
|
||||
"""Ping between all hosts.
|
||||
returns: ploss packet loss percentage"""
|
||||
return self.ping()
|
||||
return self.ping( timeout=timeout )
|
||||
|
||||
def pingPair( self ):
|
||||
"""Ping between first two hosts, useful for testing.
|
||||
@@ -467,6 +643,17 @@ class Mininet( object ):
|
||||
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
|
||||
return self.ping( hosts=hosts )
|
||||
|
||||
def pingAllFull( self ):
|
||||
"""Ping between all hosts.
|
||||
returns: ploss packet loss percentage"""
|
||||
return self.pingFull()
|
||||
|
||||
def pingPairFull( self ):
|
||||
"""Ping between first two hosts, useful for testing.
|
||||
returns: ploss packet loss percentage"""
|
||||
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
|
||||
return self.pingFull( hosts=hosts )
|
||||
|
||||
@staticmethod
|
||||
def _parseIperf( iperfOutput ):
|
||||
"""Parse iperf output and return bandwidth.
|
||||
@@ -481,7 +668,9 @@ class Mininet( object ):
|
||||
error( 'could not parse iperf output: ' + iperfOutput )
|
||||
return ''
|
||||
|
||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
|
||||
# XXX This should be cleaned up
|
||||
|
||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ):
|
||||
"""Run iperf between two hosts.
|
||||
hosts: list of hosts; if None, uses opposite hosts
|
||||
l4Type: string, one of [ TCP, UDP ]
|
||||
@@ -504,16 +693,19 @@ class Mininet( object ):
|
||||
bwArgs = '-b ' + udpBw + ' '
|
||||
elif l4Type != 'TCP':
|
||||
raise Exception( 'Unexpected l4 type: %s' % l4Type )
|
||||
if format:
|
||||
iperfArgs += '-f %s ' %format
|
||||
server.sendCmd( iperfArgs + '-s', printPid=True )
|
||||
servout = ''
|
||||
while server.lastPid is None:
|
||||
servout += server.monitor()
|
||||
while 'Connected' not in client.cmd(
|
||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||
output('waiting for iperf to start up')
|
||||
sleep(.5)
|
||||
if l4Type == 'TCP':
|
||||
while 'Connected' not in client.cmd(
|
||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||
info( 'Waiting for iperf to start up...' )
|
||||
sleep(.5)
|
||||
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
|
||||
bwArgs )
|
||||
bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
server.sendInt()
|
||||
servout += server.waitOutput()
|
||||
@@ -524,6 +716,44 @@ class Mininet( object ):
|
||||
output( '*** Results: %s\n' % result )
|
||||
return result
|
||||
|
||||
def runCpuLimitTest( self, cpu, duration=5 ):
|
||||
"""run CPU limit test with 'while true' processes.
|
||||
cpu: desired CPU fraction of each host
|
||||
duration: test duration in seconds
|
||||
returns a single list of measured CPU fractions as floats.
|
||||
"""
|
||||
pct = cpu * 100
|
||||
info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
|
||||
hosts = self.hosts
|
||||
for h in hosts:
|
||||
h.cmd( 'while true; do a=1; done &' )
|
||||
pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
|
||||
pids_str = ",".join(["%s" % pid for pid in pids])
|
||||
cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
|
||||
# It's a shame that this is what pylint prefers
|
||||
outputs = []
|
||||
for _ in range( duration ):
|
||||
sleep( 1 )
|
||||
outputs.append( quietRun( cmd ).strip() )
|
||||
for h in hosts:
|
||||
h.cmd( 'kill %1' )
|
||||
cpu_fractions = []
|
||||
for test_output in outputs:
|
||||
# Split by line. Ignore first line, which looks like this:
|
||||
# PID %CPU COMMAND\n
|
||||
for line in test_output.split('\n')[1:]:
|
||||
r = r'\d+\s*(\d+\.\d+)'
|
||||
m = re.search( r, line )
|
||||
if m is None:
|
||||
error( '*** Error: could not extract CPU fraction: %s\n' %
|
||||
line )
|
||||
return None
|
||||
cpu_fractions.append( float( m.group( 1 ) ) )
|
||||
output( '*** Results: %s\n' % cpu_fractions )
|
||||
return cpu_fractions
|
||||
|
||||
# BL: I think this can be rewritten now that we have
|
||||
# a real link class.
|
||||
def configLinkStatus( self, src, dst, status ):
|
||||
"""Change status of src <-> dst links.
|
||||
src: node name
|
||||
@@ -534,15 +764,18 @@ class Mininet( object ):
|
||||
elif dst not in self.nameToNode:
|
||||
error( 'dst not in network: %s\n' % dst )
|
||||
else:
|
||||
srcNode, dstNode = self.nameToNode[ src ], self.nameToNode[ dst ]
|
||||
connections = srcNode.connectionsTo( dstNode )
|
||||
if type( src ) is str:
|
||||
src = self.nameToNode[ src ]
|
||||
if type( dst ) is str:
|
||||
dst = self.nameToNode[ dst ]
|
||||
connections = src.connectionsTo( dst )
|
||||
if len( connections ) == 0:
|
||||
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
|
||||
for srcIntf, dstIntf in connections:
|
||||
result = srcNode.cmd( 'ifconfig', srcIntf, status )
|
||||
result = srcIntf.ifconfig( status )
|
||||
if result:
|
||||
error( 'link src status change failed: %s\n' % result )
|
||||
result = dstNode.cmd( 'ifconfig', dstIntf, status )
|
||||
result = dstIntf.ifconfig( status )
|
||||
if result:
|
||||
error( 'link dst status change failed: %s\n' % result )
|
||||
|
||||
@@ -553,26 +786,84 @@ class Mininet( object ):
|
||||
self.stop()
|
||||
return result
|
||||
|
||||
inited = False
|
||||
|
||||
# pylint thinks inited is unused
|
||||
# pylint: disable-msg=W0612
|
||||
@classmethod
|
||||
def init( cls ):
|
||||
"Initialize Mininet"
|
||||
if cls.inited:
|
||||
return
|
||||
ensureRoot()
|
||||
fixLimits()
|
||||
cls.inited = True
|
||||
|
||||
def init():
|
||||
"Initialize Mininet."
|
||||
if init.inited:
|
||||
return
|
||||
if os.getuid() != 0:
|
||||
# Note: this script must be run as root
|
||||
# Perhaps we should do so automatically!
|
||||
print "*** Mininet must run as root."
|
||||
exit( 1 )
|
||||
# If which produces no output, then mnexec is not in the path.
|
||||
# May want to loosen this to handle mnexec in the current dir.
|
||||
if not quietRun( 'which mnexec' ):
|
||||
raise Exception( "Could not find mnexec - check $PATH" )
|
||||
fixLimits()
|
||||
init.inited = True
|
||||
|
||||
init.inited = False
|
||||
class MininetWithControlNet( Mininet ):
|
||||
|
||||
# pylint: enable-msg=W0612
|
||||
"""Control network support:
|
||||
|
||||
Create an explicit control network. Currently this is only
|
||||
used/usable with the user datapath.
|
||||
|
||||
Notes:
|
||||
|
||||
1. If the controller and switches are in the same (e.g. root)
|
||||
namespace, they can just use the loopback connection.
|
||||
|
||||
2. If we can get unix domain sockets to work, we can use them
|
||||
instead of an explicit control network.
|
||||
|
||||
3. Instead of routing, we could bridge or use 'in-band' control.
|
||||
|
||||
4. Even if we dispense with this in general, it could still be
|
||||
useful for people who wish to simulate a separate control
|
||||
network (since real networks may need one!)
|
||||
|
||||
5. Basically nobody ever used this code, so it has been moved
|
||||
into its own class.
|
||||
|
||||
6. Ultimately we may wish to extend this to allow us to create a
|
||||
control network which every node's control interface is
|
||||
attached to."""
|
||||
|
||||
def configureControlNetwork( self ):
|
||||
"Configure control network."
|
||||
self.configureRoutedControlNetwork()
|
||||
|
||||
# We still need to figure out the right way to pass
|
||||
# in the control network location.
|
||||
|
||||
def configureRoutedControlNetwork( self, ip='192.168.123.1',
|
||||
prefixLen=16 ):
|
||||
"""Configure a routed control network on controller and switches.
|
||||
For use with the user datapath only right now."""
|
||||
controller = self.controllers[ 0 ]
|
||||
info( controller.name + ' <->' )
|
||||
cip = ip
|
||||
snum = ipParse( ip )
|
||||
for switch in self.switches:
|
||||
info( ' ' + switch.name )
|
||||
link = self.link( switch, controller, port1=0 )
|
||||
sintf, cintf = link.intf1, link.intf2
|
||||
switch.controlIntf = sintf
|
||||
snum += 1
|
||||
while snum & 0xff in [ 0, 255 ]:
|
||||
snum += 1
|
||||
sip = ipStr( snum )
|
||||
cintf.setIP( cip, prefixLen )
|
||||
sintf.setIP( sip, prefixLen )
|
||||
controller.setHostRoute( sip, cintf )
|
||||
switch.setHostRoute( cip, sintf )
|
||||
info( '\n' )
|
||||
info( '*** Testing control network\n' )
|
||||
while not cintf.isUp():
|
||||
info( '*** Waiting for', cintf, 'to come up\n' )
|
||||
sleep( 1 )
|
||||
for switch in self.switches:
|
||||
while not sintf.isUp():
|
||||
info( '*** Waiting for', sintf, 'to come up\n' )
|
||||
sleep( 1 )
|
||||
if self.ping( hosts=[ switch, controller ] ) != 0:
|
||||
error( '*** Error: control network test failed\n' )
|
||||
exit( 1 )
|
||||
info( '\n' )
|
||||
|
||||
+961
-306
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Node Library for Mininet
|
||||
|
||||
This contains additional Node types which you may find to be useful.
|
||||
"""
|
||||
|
||||
from mininet.node import Node, Switch
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
class LinuxBridge( Switch ):
|
||||
"Linux Bridge (with optional spanning tree)"
|
||||
|
||||
nextPrio = 100 # next bridge priority for spanning tree
|
||||
|
||||
def __init__( self, name, stp=False, prio=None, **kwargs ):
|
||||
"""stp: use spanning tree protocol? (default False)
|
||||
prio: optional explicit bridge priority for STP"""
|
||||
self.stp = stp
|
||||
if prio:
|
||||
self.prio = prio
|
||||
else:
|
||||
self.prio = LinuxBridge.nextPrio
|
||||
LinuxBridge.nextPrio += 1
|
||||
Switch.__init__( self, name, **kwargs )
|
||||
|
||||
def connected( self ):
|
||||
"Are we forwarding yet?"
|
||||
if self.stp:
|
||||
return 'forwarding' in self.cmd( 'brctl showstp', self )
|
||||
else:
|
||||
return True
|
||||
|
||||
def start( self, controllers ):
|
||||
self.cmd( 'ifconfig', self, 'down' )
|
||||
self.cmd( 'brctl delbr', self )
|
||||
self.cmd( 'brctl addbr', self )
|
||||
if self.stp:
|
||||
self.cmd( 'brctl setbridgeprio', self.prio )
|
||||
self.cmd( 'brctl stp', self, 'on' )
|
||||
for i in self.intfList():
|
||||
if self.name in i.name:
|
||||
self.cmd( 'brctl addif', self, i )
|
||||
self.cmd( 'ifconfig', self, 'up' )
|
||||
|
||||
def stop( self ):
|
||||
self.cmd( 'ifconfig', self, 'down' )
|
||||
self.cmd( 'brctl delbr', self )
|
||||
|
||||
class NAT( Node ):
|
||||
"""NAT: Provides connectivity to external network"""
|
||||
|
||||
def __init__( self, name, inetIntf='eth0', subnet='10.0/8', localIntf=None, **params):
|
||||
super( NAT, self ).__init__( name, **params )
|
||||
|
||||
"""Start NAT/forwarding between Mininet and external network
|
||||
inetIntf: interface for internet access
|
||||
subnet: Mininet subnet (default 10.0/8)="""
|
||||
self.inetIntf = inetIntf
|
||||
self.subnet = subnet
|
||||
self.localIntf = localIntf
|
||||
|
||||
def config( self, **params ):
|
||||
super( NAT, self).config( **params )
|
||||
"""Configure the NAT and iptables"""
|
||||
|
||||
if not self.localIntf:
|
||||
self.localIntf = self.defaultIntf()
|
||||
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
# Flush any currently active rules
|
||||
# TODO: is this safe?
|
||||
self.cmd( 'iptables -F' )
|
||||
self.cmd( 'iptables -t nat -F' )
|
||||
|
||||
# Create default entries for unmatched traffic
|
||||
self.cmd( 'iptables -P INPUT ACCEPT' )
|
||||
self.cmd( 'iptables -P OUTPUT ACCEPT' )
|
||||
self.cmd( 'iptables -P FORWARD DROP' )
|
||||
|
||||
# Configure NAT
|
||||
self.cmd( 'iptables -I FORWARD -i', self.localIntf, '-d', self.subnet, '-j DROP' )
|
||||
self.cmd( 'iptables -A FORWARD -i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -A FORWARD -i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -t nat -A POSTROUTING -o ', self.inetIntf, '-j MASQUERADE' )
|
||||
|
||||
# Instruct the kernel to perform forwarding
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
||||
|
||||
# Prevent network-manager from messing with our interface
|
||||
# by specifying manual configuration in /etc/network/interfaces
|
||||
intf = self.localIntf
|
||||
cfile = '/etc/network/interfaces'
|
||||
line = '\niface %s inet manual\n' % intf
|
||||
config = open( cfile ).read()
|
||||
if ( line ) not in config:
|
||||
info( '*** 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
|
||||
self.cmd( 'service network-manager restart' )
|
||||
|
||||
def terminate( self ):
|
||||
"""Stop NAT/forwarding between Mininet and external network"""
|
||||
# Flush any currently active rules
|
||||
# TODO: is this safe?
|
||||
self.cmd( 'iptables -F' )
|
||||
self.cmd( 'iptables -t nat -F' )
|
||||
|
||||
# Instruct the kernel to stop forwarding
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
super( NAT, self ).terminate()
|
||||
|
||||
+52
-32
@@ -1,60 +1,80 @@
|
||||
"""
|
||||
Terminal creation and cleanup.
|
||||
Utility functions to run a term (connected via screen(1)) on each host.
|
||||
Utility functions to run a terminal (connected via socat(1)) on each host.
|
||||
|
||||
Requires GNU screen(1) and xterm(1).
|
||||
Requires socat(1) and xterm(1).
|
||||
Optionally uses gnome-terminal.
|
||||
"""
|
||||
|
||||
import re
|
||||
from subprocess import Popen
|
||||
from os import environ
|
||||
|
||||
from mininet.log import error
|
||||
from mininet.util import quietRun
|
||||
from mininet.util import quietRun, errRun
|
||||
|
||||
def quoteArg( arg ):
|
||||
"Quote an argument if it contains spaces."
|
||||
return repr( arg ) if ' ' in arg else arg
|
||||
def tunnelX11( node, display=None):
|
||||
"""Create an X11 tunnel from node:6000 to the root host
|
||||
display: display on root host (optional)
|
||||
returns: node $DISPLAY, Popen object for tunnel"""
|
||||
if display is None and 'DISPLAY' in environ:
|
||||
display = environ[ 'DISPLAY' ]
|
||||
if display is None:
|
||||
error( "Error: Cannot connect to display\n" )
|
||||
return None, None
|
||||
host, screen = display.split( ':' )
|
||||
# Unix sockets should work
|
||||
if not host or host == 'unix':
|
||||
# GDM3 doesn't put credentials in .Xauthority,
|
||||
# so allow root to just connect
|
||||
quietRun( 'xhost +si:localuser:root' )
|
||||
return display, None
|
||||
else:
|
||||
# Create a tunnel for the TCP connection
|
||||
port = 6000 + int( float( screen ) )
|
||||
connection = r'TCP\:%s\:%s' % ( host, port )
|
||||
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
|
||||
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
|
||||
return 'localhost:' + screen, node.popen( cmd )
|
||||
|
||||
def makeTerm( node, title='Node', term='xterm' ):
|
||||
"""Run screen on a node, and hook up a terminal.
|
||||
def makeTerm( node, title='Node', term='xterm', display=None ):
|
||||
"""Create an X11 tunnel to the node and start up a terminal.
|
||||
node: Node object
|
||||
title: base title
|
||||
term: 'xterm' or 'gterm'
|
||||
returns: process created"""
|
||||
returns: two Popen objects, tunnel and terminal"""
|
||||
title += ': ' + node.name
|
||||
if not node.inNamespace:
|
||||
title += ' (root)'
|
||||
cmds = {
|
||||
'xterm': [ 'xterm', '-title', title, '-e' ],
|
||||
'gterm': [ 'gnome-terminal', '--title', title, '-e' ]
|
||||
'xterm': [ 'xterm', '-title', title, '-display' ],
|
||||
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
|
||||
}
|
||||
if term not in cmds:
|
||||
error( 'invalid terminal type: %s' % term )
|
||||
return
|
||||
if not node.execed:
|
||||
node.cmd( 'screen -dmS ' + 'mininet.' + node.name)
|
||||
args = [ 'screen', '-D', '-RR', '-S', 'mininet.' + node.name ]
|
||||
else:
|
||||
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
|
||||
if term == 'gterm':
|
||||
# Compress these for gnome-terminal, which expects one token
|
||||
# to follow the -e option
|
||||
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
|
||||
return Popen( cmds[ term ] + args )
|
||||
display, tunnel = tunnelX11( node, display )
|
||||
if display is None:
|
||||
return []
|
||||
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
|
||||
return [ tunnel, term ] if tunnel else [ term ]
|
||||
|
||||
def runX11( node, cmd ):
|
||||
"Run an X11 client on a node"
|
||||
_display, tunnel = tunnelX11( node )
|
||||
if _display is None:
|
||||
return []
|
||||
popen = node.popen( cmd )
|
||||
return [ tunnel, popen ]
|
||||
|
||||
def cleanUpScreens():
|
||||
"Remove moldy old screen sessions."
|
||||
r = r'(\d+\.mininet\.[hsc]\d+)'
|
||||
output = quietRun( 'screen -ls' ).split( '\n' )
|
||||
for line in output:
|
||||
m = re.search( r, line )
|
||||
if m:
|
||||
quietRun( 'screen -S ' + m.group( 1 ) + ' -X quit' )
|
||||
"Remove moldy socat X11 tunnels."
|
||||
errRun( "pkill -9 -f mnexec.*socat" )
|
||||
|
||||
def makeTerms( nodes, title='Node', term='xterm' ):
|
||||
"""Create terminals.
|
||||
nodes: list of Node objects
|
||||
title: base title for each
|
||||
returns: list of created terminal processes"""
|
||||
return [ makeTerm( node, title, term ) for node in nodes ]
|
||||
returns: list of created tunnel/terminal processes"""
|
||||
terms = []
|
||||
for node in nodes:
|
||||
terms += makeTerm( node, title, term )
|
||||
return terms
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Run all mininet core 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
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
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
|
||||
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
# 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
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Package: mininet
|
||||
Test creation and pings for topologies with link and/or CPU options."""
|
||||
|
||||
import unittest
|
||||
from functools import partial
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import OVSSwitch, UserSwitch, IVSSwitch
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.link import TCLink
|
||||
from mininet.topo import Topo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import quietRun
|
||||
|
||||
# Number of hosts for each test
|
||||
N = 2
|
||||
|
||||
|
||||
class SingleSwitchOptionsTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def __init__(self, n=2, hopts=None, lopts=None):
|
||||
if not hopts:
|
||||
hopts = {}
|
||||
if not lopts:
|
||||
lopts = {}
|
||||
Topo.__init__(self, hopts=hopts, lopts=lopts)
|
||||
switch = self.addSwitch('s1')
|
||||
for h in range(n):
|
||||
host = self.addHost('h%s' % (h + 1))
|
||||
self.addLink(host, switch)
|
||||
|
||||
# Tell pylint not to complain about calls to other class
|
||||
# pylint: disable=E1101
|
||||
|
||||
class testOptionsTopoCommon( object ):
|
||||
"""Verify ability to create networks with host and link options
|
||||
(common code)."""
|
||||
|
||||
switchClass = None # overridden in subclasses
|
||||
|
||||
def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
|
||||
"Generic topology-with-options test runner."
|
||||
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
|
||||
lopts=lopts ),
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
switch=self.switchClass )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
|
||||
def assertWithinTolerance(self, measured, expected, tolerance_frac):
|
||||
"""Check that a given value is within a tolerance of expected
|
||||
tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
|
||||
"""
|
||||
self.assertGreaterEqual( float(measured),
|
||||
float(expected) * tolerance_frac )
|
||||
self.assertLessEqual( float( measured ),
|
||||
float(expected) + (1-tolerance_frac)
|
||||
* float( expected ) )
|
||||
|
||||
def testCPULimits( self ):
|
||||
"Verify topology creation with CPU limits set for both schedulers."
|
||||
CPU_FRACTION = 0.1
|
||||
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
|
||||
hopts = { 'cpu': CPU_FRACTION }
|
||||
#self.runOptionsTopoTest( N, hopts=hopts )
|
||||
|
||||
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
|
||||
host=CPULimitedHost, switch=self.switchClass )
|
||||
mn.start()
|
||||
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
|
||||
mn.stop()
|
||||
for pct in results:
|
||||
#divide cpu by 100 to convert from percentage to fraction
|
||||
self.assertWithinTolerance( pct/100, CPU_FRACTION, CPU_TOLERANCE )
|
||||
|
||||
def testLinkBandwidth( self ):
|
||||
"Verify that link bandwidths are accurate within a bound."
|
||||
BW = .5 # Mbps
|
||||
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
|
||||
# Verify ability to create limited-link topo first;
|
||||
lopts = { 'bw': BW, 'use_htb': True }
|
||||
# Also verify correctness of limit limitng within a bound.
|
||||
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||
link=TCLink, switch=self.switchClass )
|
||||
bw_strs = mn.run( mn.iperf, format='m' )
|
||||
for bw_str in bw_strs:
|
||||
bw = float( bw_str.split(' ')[0] )
|
||||
self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
|
||||
|
||||
def testLinkDelay( self ):
|
||||
"Verify that link delays are accurate within a bound."
|
||||
DELAY_MS = 15
|
||||
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
|
||||
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
|
||||
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||
link=TCLink, switch=self.switchClass, autoStaticArp=True )
|
||||
ping_delays = mn.run( mn.pingFull )
|
||||
test_outputs = ping_delays[0]
|
||||
# Ignore unused variables below
|
||||
# pylint: disable-msg=W0612
|
||||
node, dest, ping_outputs = test_outputs
|
||||
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
|
||||
self.assertEqual( sent, received )
|
||||
# pylint: enable-msg=W0612
|
||||
for rttval in [rttmin, rttavg, rttmax]:
|
||||
# Multiply delay by 4 to cover there & back on two links
|
||||
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
|
||||
DELAY_TOLERANCE)
|
||||
|
||||
|
||||
def testLinkLoss( self ):
|
||||
"Verify that we see packet drops with a high configured loss rate."
|
||||
LOSS_PERCENT = 99
|
||||
REPS = 1
|
||||
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
|
||||
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
switch=self.switchClass )
|
||||
# Drops are probabilistic, but the chance of no dropped packets is
|
||||
# 1 in 100 million with 4 hops for a link w/99% loss.
|
||||
dropped_total = 0
|
||||
mn.start()
|
||||
for _ in range(REPS):
|
||||
dropped_total += mn.ping(timeout='1')
|
||||
mn.stop()
|
||||
self.assertGreater( dropped_total, 0 )
|
||||
|
||||
def testMostOptions( self ):
|
||||
"Verify topology creation with most link options and CPU limits."
|
||||
lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
|
||||
hopts = { 'cpu': 0.5 / N }
|
||||
self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )
|
||||
|
||||
# pylint: enable=E1101
|
||||
|
||||
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
|
||||
"""Verify ability to create networks with host and link options
|
||||
(OVS kernel switch)."""
|
||||
switchClass = OVSSwitch
|
||||
|
||||
@unittest.skip( 'Skipping OVS user switch test for now' )
|
||||
class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
|
||||
"""Verify ability to create networks with host and link options
|
||||
(OVS user switch)."""
|
||||
switchClass = partial( OVSSwitch, datapath='user' )
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
|
||||
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
|
||||
"Verify ability to create networks with host and link options (IVS)."
|
||||
switchClass = IVSSwitch
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
|
||||
'Reference user switch is not installed' )
|
||||
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
|
||||
"Verify ability to create networks with host and link options (UserSwitch)."
|
||||
switchClass = UserSwitch
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
+74
-37
@@ -4,58 +4,95 @@
|
||||
Test creation and all-pairs ping for each included mininet topo type."""
|
||||
|
||||
import unittest
|
||||
from functools import partial
|
||||
|
||||
from mininet.net import init, Mininet
|
||||
from mininet.node import Host, Controller, ControllerParams
|
||||
# from mininet.node import KernelSwitch
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Host, Controller
|
||||
from mininet.node import UserSwitch, OVSSwitch, IVSSwitch
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import quietRun
|
||||
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
'ovsk': OVSKernelSwitch,
|
||||
# 'kernel': KernelSwitch
|
||||
}
|
||||
# Tell pylint not to complain about calls to other class
|
||||
# pylint: disable=E1101
|
||||
|
||||
class testSingleSwitchCommon( object ):
|
||||
"Test ping with single switch topology (common code)."
|
||||
|
||||
class testSingleSwitch( unittest.TestCase ):
|
||||
"For each datapath type, test ping with single switch topologies."
|
||||
switchClass = None # overridden in subclasses
|
||||
|
||||
def testMinimal( self ):
|
||||
"Ping test with both datapaths on minimal topology"
|
||||
init()
|
||||
for switch in SWITCHES.values():
|
||||
controllerParams = ControllerParams( '10.0.0.0', 8 )
|
||||
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller,
|
||||
controllerParams )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
"Ping test on minimal topology"
|
||||
mn = Mininet( SingleSwitchTopo(), self.switchClass, Host, Controller )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
|
||||
def testSingle5( self ):
|
||||
"Ping test with both datapaths on 5-host single-switch topology"
|
||||
init()
|
||||
for switch in SWITCHES.values():
|
||||
controllerParams = ControllerParams( '10.0.0.0', 8 )
|
||||
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller,
|
||||
controllerParams )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
"Ping test on 5-host single-switch topology"
|
||||
mn = Mininet( SingleSwitchTopo( k=5 ), self.switchClass, Host,
|
||||
Controller )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
|
||||
# pylint: enable=E1101
|
||||
|
||||
class testSingleSwitchOVSKernel( testSingleSwitchCommon, unittest.TestCase ):
|
||||
"Test ping with single switch topology (OVS kernel switch)."
|
||||
switchClass = OVSSwitch
|
||||
|
||||
class testSingleSwitchOVSUser( testSingleSwitchCommon, unittest.TestCase ):
|
||||
"Test ping with single switch topology (OVS user switch)."
|
||||
switchClass = partial( OVSSwitch, datapath='user' )
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
|
||||
class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ):
|
||||
"Test ping with single switch topology (IVS switch)."
|
||||
switchClass = IVSSwitch
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
|
||||
'Reference user switch is not installed' )
|
||||
class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
|
||||
"Test ping with single switch topology (Userspace switch)."
|
||||
switchClass = UserSwitch
|
||||
|
||||
|
||||
class testLinear( unittest.TestCase ):
|
||||
"For each datapath type, test all-pairs ping with LinearNet."
|
||||
# Tell pylint not to complain about calls to other class
|
||||
# pylint: disable=E1101
|
||||
|
||||
class testLinearCommon( object ):
|
||||
"Test all-pairs ping with LinearNet (common code)."
|
||||
|
||||
switchClass = None # overridden in subclasses
|
||||
|
||||
def testLinear5( self ):
|
||||
"Ping test with both datapaths on a 5-switch topology"
|
||||
init()
|
||||
for switch in SWITCHES.values():
|
||||
controllerParams = ControllerParams( '10.0.0.0', 8 )
|
||||
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller,
|
||||
controllerParams )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
"Ping test on a 5-switch topology"
|
||||
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller, waitConnected=True )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
|
||||
# pylint: enable=E1101
|
||||
|
||||
|
||||
class testLinearOVSKernel( testLinearCommon, unittest.TestCase ):
|
||||
"Test all-pairs ping with LinearNet (OVS kernel switch)."
|
||||
switchClass = OVSSwitch
|
||||
|
||||
class testLinearOVSUser( testLinearCommon, unittest.TestCase ):
|
||||
"Test all-pairs ping with LinearNet (OVS user switch)."
|
||||
switchClass = partial( OVSSwitch, datapath='user' )
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
|
||||
class testLinearIVS( testLinearCommon, unittest.TestCase ):
|
||||
"Test all-pairs ping with LinearNet (IVS switch)."
|
||||
switchClass = IVSSwitch
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
|
||||
'Reference user switch is not installed' )
|
||||
class testLinearUserspace( testLinearCommon, unittest.TestCase ):
|
||||
"Test all-pairs ping with LinearNet (Userspace switch)."
|
||||
switchClass = UserSwitch
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('warning')
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
|
||||
Executable
+331
@@ -0,0 +1,331 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for the Mininet Walkthrough
|
||||
|
||||
TODO: missing xterm test
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import os
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
# PART 1
|
||||
def testHelp( self ):
|
||||
"Check the usage message"
|
||||
p = pexpect.spawn( 'mn -h' )
|
||||
index = p.expect( [ 'Usage: mn', pexpect.EOF ] )
|
||||
self.assertEqual( index, 0 )
|
||||
|
||||
def testWireshark( self ):
|
||||
"Use tshark to test the of dissector"
|
||||
tshark = pexpect.spawn( 'tshark -i lo -R of' )
|
||||
tshark.expect( 'Capturing on lo' )
|
||||
mn = pexpect.spawn( 'mn --test pingall' )
|
||||
mn.expect( '0% dropped' )
|
||||
tshark.expect( 'OFP 74 Hello' )
|
||||
tshark.sendintr()
|
||||
|
||||
def testBasic( self ):
|
||||
"Test basic CLI commands (help, nodes, net, dump)"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# help command
|
||||
p.sendline( 'help' )
|
||||
index = p.expect( [ 'commands', self.prompt ] )
|
||||
self.assertEqual( index, 0, 'No output for "help" command')
|
||||
# nodes command
|
||||
p.sendline( 'nodes' )
|
||||
p.expect( '([chs]\d ?){4}' )
|
||||
nodes = p.match.group( 0 ).split()
|
||||
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
|
||||
p.expect( self.prompt )
|
||||
# net command
|
||||
p.sendline( 'net' )
|
||||
expected = [ x for x in nodes ]
|
||||
while len( expected ) > 0:
|
||||
index = p.expect( expected )
|
||||
node = p.match.group( 0 )
|
||||
expected.remove( node )
|
||||
p.expect( '\n' )
|
||||
self.assertEqual( len( expected ), 0, '"nodes" and "net" differ')
|
||||
p.expect( self.prompt )
|
||||
# dump command
|
||||
p.sendline( 'dump' )
|
||||
expected = [ '<\w+ (%s)' % n for n in nodes ]
|
||||
actual = []
|
||||
for _ in nodes:
|
||||
index = p.expect( expected )
|
||||
node = p.match.group( 1 )
|
||||
actual.append( node )
|
||||
p.expect( '\n' )
|
||||
self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testHostCommands( self ):
|
||||
"Test ifconfig and ps on h1 and s1"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
# h1 ifconfig
|
||||
p.sendline( 'h1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index == 0 or index == 3:
|
||||
ifcount += 1
|
||||
elif index == 1:
|
||||
self.fail( 's1 interface displayed in "h1 ifconfig"' )
|
||||
elif index == 2:
|
||||
self.fail( 'eth0 displayed in "h1 ifconfig"' )
|
||||
else:
|
||||
break
|
||||
self.assertEqual( ifcount, 2, 'Missing interfaces on h1')
|
||||
# s1 ifconfig
|
||||
p.sendline( 's1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index == 0:
|
||||
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
||||
elif index == 1 or index == 2 or index == 3:
|
||||
ifcount += 1
|
||||
else:
|
||||
break
|
||||
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
|
||||
# h1 ps
|
||||
p.sendline( 'h1 ps -a' )
|
||||
p.expect( self.prompt )
|
||||
h1Output = p.before
|
||||
# s1 ps
|
||||
p.sendline( 's1 ps -a' )
|
||||
p.expect( self.prompt )
|
||||
s1Output = p.before
|
||||
# strip command from ps output
|
||||
h1Output = h1Output.split( '\n', 1 )[ 1 ]
|
||||
s1Output = s1Output.split( '\n', 1 )[ 1 ]
|
||||
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testConnectivity( self ):
|
||||
"Test ping and pingall"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( '1 packets transmitted, 1 received' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect( '0% dropped' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testSimpleHTTP( self ):
|
||||
"Start an HTTP server on h1 and wget from h2"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( ' h2 wget -O - h1' )
|
||||
p.expect( '200 OK' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 kill %python' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
# PART 2
|
||||
def testRegressionRun( self ):
|
||||
"Test pingpair (0% drop) and iperf (bw > 0) regression tests"
|
||||
# test pingpair
|
||||
p = pexpect.spawn( 'mn --test pingpair' )
|
||||
p.expect( '0% dropped' )
|
||||
p.expect( pexpect.EOF )
|
||||
# test iperf
|
||||
p = pexpect.spawn( 'mn --test iperf' )
|
||||
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw > 0 )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testTopoChange( self ):
|
||||
"Test pingall on single,3 and linear,4 topos"
|
||||
# testing single,3
|
||||
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
|
||||
p.expect( '(\d+)/(\d+) received')
|
||||
received = int( p.match.group( 1 ) )
|
||||
sent = int( p.match.group( 2 ) )
|
||||
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
|
||||
self.assertEqual( sent, received, 'Dropped packets in single,3')
|
||||
p.expect( pexpect.EOF )
|
||||
# testing linear,4
|
||||
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
|
||||
p.expect( '(\d+)/(\d+) received')
|
||||
received = int( p.match.group( 1 ) )
|
||||
sent = int( p.match.group( 2 ) )
|
||||
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
|
||||
self.assertEqual( sent, received, 'Dropped packets in linear,4')
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testLinkChange( self ):
|
||||
"Test TCLink bw and delay"
|
||||
p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
|
||||
# test bw
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'iperf' )
|
||||
p.expect( "Results: \['([\d\.]+) Mbits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
|
||||
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
|
||||
p.expect( self.prompt )
|
||||
# test delay
|
||||
p.sendline( 'h1 ping -c 4 h2' )
|
||||
p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
|
||||
delay = float( p.match.group( 2 ) )
|
||||
self.assertTrue( delay > 40, 'Delay < 40ms' )
|
||||
self.assertTrue( delay < 45, 'Delay > 40ms' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testVerbosity( self ):
|
||||
"Test debug and output verbosity"
|
||||
# test output
|
||||
p = pexpect.spawn( 'mn -v output' )
|
||||
p.expect( self.prompt )
|
||||
self.assertEqual( len( p.before ), 0, 'Too much output for "output"' )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
# test debug
|
||||
p = pexpect.spawn( 'mn -v debug --test none' )
|
||||
p.expect( pexpect.EOF )
|
||||
lines = p.before.split( '\n' )
|
||||
self.assertTrue( len( lines ) > 100, "Debug output is too short" )
|
||||
|
||||
def testCustomTopo( self ):
|
||||
"Start Mininet using a custom topo, then run pingall"
|
||||
custom = os.path.dirname( os.path.realpath( __file__ ) )
|
||||
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
|
||||
custom = os.path.normpath( custom )
|
||||
p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom )
|
||||
p.expect( '0% dropped' )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testStaticMAC( self ):
|
||||
"Verify that MACs are set to easy to read numbers"
|
||||
p = pexpect.spawn( 'mn --mac' )
|
||||
p.expect( self.prompt )
|
||||
for i in range( 1, 3 ):
|
||||
p.sendline( 'h%d ifconfig' % i )
|
||||
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
|
||||
p.expect( self.prompt )
|
||||
|
||||
def testSwitches( self ):
|
||||
"Run iperf test using user and ovsk switches"
|
||||
switches = [ 'user', 'ovsk' ]
|
||||
for sw in switches:
|
||||
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
|
||||
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw > 0 )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testBenchmark( self ):
|
||||
"Run benchmark and verify that it takes less than 2 seconds"
|
||||
p = pexpect.spawn( 'mn --test none' )
|
||||
p.expect( 'completed in ([\d\.]+) seconds' )
|
||||
time = float( p.match.group( 1 ) )
|
||||
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
|
||||
|
||||
def testOwnNamespace( self ):
|
||||
"Test running user switch in its own namespace"
|
||||
p = pexpect.spawn( 'mn --innamespace --switch user' )
|
||||
p.expect( self.prompt )
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
p.sendline( 's1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index == 1 or index == 3:
|
||||
ifcount += 1
|
||||
elif index == 0:
|
||||
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
||||
elif index == 2:
|
||||
self.fail( 'eth0 displayed in "s1 ifconfig"' )
|
||||
else:
|
||||
break
|
||||
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
|
||||
# verify that all hosts a reachable
|
||||
p.sendline( 'pingall' )
|
||||
p.expect( '(\d+)% dropped' )
|
||||
dropped = int( p.match.group( 1 ) )
|
||||
self.assertEqual( dropped, 0, 'pingall failed')
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
# PART 3
|
||||
def testPythonInterpreter( self ):
|
||||
"Test py and px by checking IP for h1 and adding h3"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# test host IP
|
||||
p.sendline( 'py h1.IP()' )
|
||||
p.expect( '10.0.0.1' )
|
||||
p.expect( self.prompt )
|
||||
# test adding host
|
||||
p.sendline( "px net.addHost('h3')" )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( "px net.addLink(s1, h3)" )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'net' )
|
||||
p.expect( 'h3' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'py h3.MAC()' )
|
||||
p.expect( '([a-f0-9]{2}:?){6}' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testLink( self ):
|
||||
"Test link CLI command using ping"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'link s1 h1 down' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( 'unreachable' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'link s1 h1 up' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( '0% packet loss' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
@unittest.skipUnless( os.path.exists( '/tmp/pox' ) or
|
||||
'1 received' in quietRun( 'ping -c 1 github.com' ),
|
||||
'Github is not reachable; cannot download Pox' )
|
||||
def testRemoteController( self ):
|
||||
"Test Mininet using Pox controller"
|
||||
if not os.path.exists( '/tmp/pox' ):
|
||||
p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
|
||||
p.expect( pexpect.EOF )
|
||||
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
|
||||
net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
|
||||
net.expect( '0% dropped' )
|
||||
net.expect( pexpect.EOF )
|
||||
pox.sendintr()
|
||||
pox.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+184
-333
@@ -11,395 +11,246 @@ A Topo object can be a topology database for NOX, can represent a physical
|
||||
setup for testing, and can even be emulated with the Mininet package.
|
||||
'''
|
||||
|
||||
# BL: we may have to fix compatibility here.
|
||||
# networkx is also a fairly heavyweight dependency
|
||||
# from networkx.classes.graph import Graph
|
||||
from mininet.util import irange, natural, naturalSeq
|
||||
|
||||
from networkx import Graph
|
||||
from mininet.node import SWITCH_PORT_BASE
|
||||
class MultiGraph( object ):
|
||||
"Utility class to track nodes and edges - replaces networkx.Graph"
|
||||
|
||||
class NodeID(object):
|
||||
'''Topo node identifier.'''
|
||||
def __init__( self ):
|
||||
self.data = {}
|
||||
|
||||
def __init__(self, dpid = None):
|
||||
'''Init.
|
||||
def add_node( self, node ):
|
||||
"Add node to graph"
|
||||
self.data.setdefault( node, [] )
|
||||
|
||||
@param dpid dpid
|
||||
'''
|
||||
# DPID-compatible hashable identifier: opaque 64-bit unsigned int
|
||||
self.dpid = dpid
|
||||
def add_edge( self, src, dest ):
|
||||
"Add edge to graph"
|
||||
src, dest = sorted( ( src, dest ) )
|
||||
self.add_node( src )
|
||||
self.add_node( dest )
|
||||
self.data[ src ].append( dest )
|
||||
|
||||
def __str__(self):
|
||||
'''String conversion.
|
||||
def nodes( self ):
|
||||
"Return list of graph nodes"
|
||||
return self.data.keys()
|
||||
|
||||
@return str dpid as string
|
||||
'''
|
||||
return str(self.dpid)
|
||||
def edges( self ):
|
||||
"Iterator: return graph edges"
|
||||
for src in self.data.keys():
|
||||
for dest in self.data[ src ]:
|
||||
yield ( src, dest )
|
||||
|
||||
def name_str(self):
|
||||
'''Name conversion.
|
||||
|
||||
@return name name as string
|
||||
'''
|
||||
return str(self.dpid)
|
||||
|
||||
def ip_str(self):
|
||||
'''Name conversion.
|
||||
|
||||
@return ip ip as string
|
||||
'''
|
||||
hi = (self.dpid & 0xff0000) >> 16
|
||||
mid = (self.dpid & 0xff00) >> 8
|
||||
lo = self.dpid & 0xff
|
||||
return "10.%i.%i.%i" % (hi, mid, lo)
|
||||
|
||||
|
||||
class Node(object):
|
||||
'''Node-specific vertex metadata for a Topo object.'''
|
||||
|
||||
def __init__(self, connected = False, admin_on = True,
|
||||
power_on = True, fault = False, is_switch = True):
|
||||
'''Init.
|
||||
|
||||
@param connected actively connected to controller
|
||||
@param admin_on administratively on or off
|
||||
@param power_on powered on or off
|
||||
@param fault fault seen on node
|
||||
@param is_switch switch or host
|
||||
'''
|
||||
self.connected = connected
|
||||
self.admin_on = admin_on
|
||||
self.power_on = power_on
|
||||
self.fault = fault
|
||||
self.is_switch = is_switch
|
||||
|
||||
|
||||
class Edge(object):
|
||||
'''Edge-specific metadata for a StructuredTopo graph.'''
|
||||
|
||||
def __init__(self, admin_on = True, power_on = True, fault = False):
|
||||
'''Init.
|
||||
|
||||
@param admin_on administratively on or off; defaults to True
|
||||
@param power_on powered on or off; defaults to True
|
||||
@param fault fault seen on edge; defaults to False
|
||||
'''
|
||||
self.admin_on = admin_on
|
||||
self.power_on = power_on
|
||||
self.fault = fault
|
||||
def __getitem__( self, node ):
|
||||
"Return link dict for the given node"
|
||||
return self.data[node]
|
||||
|
||||
|
||||
class Topo(object):
|
||||
'''Data center network representation for structured multi-trees.'''
|
||||
"Data center network representation for structured multi-trees."
|
||||
|
||||
def __init__(self):
|
||||
'''Create Topo object.
|
||||
|
||||
'''
|
||||
self.g = Graph()
|
||||
self.node_info = {} # dpids hash to Node objects
|
||||
self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
|
||||
def __init__(self, *args, **params):
|
||||
"""Topo object.
|
||||
Optional named parameters:
|
||||
hinfo: default host options
|
||||
sopts: default switch options
|
||||
lopts: default link options
|
||||
calls build()"""
|
||||
self.g = MultiGraph()
|
||||
self.node_info = {}
|
||||
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
|
||||
self.hopts = params.pop( 'hopts', {} )
|
||||
self.sopts = params.pop( 'sopts', {} )
|
||||
self.lopts = params.pop( 'lopts', {} )
|
||||
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
||||
self.id_gen = NodeID # class used to generate dpid
|
||||
self.build( *args, **params )
|
||||
|
||||
def add_node(self, dpid, node):
|
||||
'''Add Node to graph.
|
||||
def build( self, *args, **params ):
|
||||
"Override this method to build your topology."
|
||||
pass
|
||||
|
||||
@param dpid dpid
|
||||
@param node Node object
|
||||
'''
|
||||
self.g.add_node(dpid)
|
||||
self.node_info[dpid] = node
|
||||
def addNode(self, name, **opts):
|
||||
"""Add Node to graph.
|
||||
name: name
|
||||
opts: node options
|
||||
returns: node name"""
|
||||
self.g.add_node(name)
|
||||
self.node_info[name] = opts
|
||||
return name
|
||||
|
||||
def add_edge(self, src, dst, edge = None):
|
||||
'''Add edge (Node, Node) to graph.
|
||||
def addHost(self, name, **opts):
|
||||
"""Convenience method: Add host to graph.
|
||||
name: host name
|
||||
opts: host options
|
||||
returns: host name"""
|
||||
if not opts and self.hopts:
|
||||
opts = self.hopts
|
||||
return self.addNode(name, **opts)
|
||||
|
||||
@param src src dpid
|
||||
@param dst dst dpid
|
||||
@param edge Edge object
|
||||
'''
|
||||
src, dst = tuple(sorted([src, dst]))
|
||||
self.g.add_edge(src, dst)
|
||||
if not edge:
|
||||
edge = Edge()
|
||||
self.edge_info[(src, dst)] = edge
|
||||
self.add_port(src, dst)
|
||||
def addSwitch(self, name, **opts):
|
||||
"""Convenience method: Add switch to graph.
|
||||
name: switch name
|
||||
opts: switch options
|
||||
returns: switch name"""
|
||||
if not opts and self.sopts:
|
||||
opts = self.sopts
|
||||
result = self.addNode(name, isSwitch=True, **opts)
|
||||
return result
|
||||
|
||||
def add_port(self, src, dst):
|
||||
def addLink(self, node1, node2, port1=None, port2=None,
|
||||
**opts):
|
||||
"""node1, node2: nodes to link together
|
||||
port1, port2: ports (optional)
|
||||
opts: link options (optional)
|
||||
returns: link info key"""
|
||||
if not opts and self.lopts:
|
||||
opts = self.lopts
|
||||
self.addPort(node1, node2, port1, port2)
|
||||
key = tuple(self.sorted([node1, node2]))
|
||||
self.link_info[key] = opts
|
||||
self.g.add_edge(*key)
|
||||
return key
|
||||
|
||||
def addPort(self, src, dst, sport=None, dport=None):
|
||||
'''Generate port mapping for new edge.
|
||||
|
||||
@param src source switch DPID
|
||||
@param dst destination switch DPID
|
||||
@param src source switch name
|
||||
@param dst destination switch name
|
||||
'''
|
||||
src_base = SWITCH_PORT_BASE if self.is_switch(src) else 0
|
||||
dst_base = SWITCH_PORT_BASE if self.is_switch(dst) else 0
|
||||
if src not in self.ports:
|
||||
self.ports[src] = {}
|
||||
if dst not in self.ports[src]:
|
||||
# num outlinks
|
||||
self.ports[src][dst] = len(self.ports[src]) + src_base
|
||||
if dst not in self.ports:
|
||||
self.ports[dst] = {}
|
||||
if src not in self.ports[dst]:
|
||||
# num outlinks
|
||||
self.ports[dst][src] = len(self.ports[dst]) + dst_base
|
||||
self.ports.setdefault(src, {})
|
||||
self.ports.setdefault(dst, {})
|
||||
# New port: number of outlinks + base
|
||||
src_base = 1 if self.isSwitch(src) else 0
|
||||
dst_base = 1 if self.isSwitch(dst) else 0
|
||||
if sport is None:
|
||||
sport = len(self.ports[src]) + src_base
|
||||
if dport is None:
|
||||
dport = len(self.ports[dst]) + dst_base
|
||||
self.ports[src][dst] = sport
|
||||
self.ports[dst][src] = dport
|
||||
|
||||
def node_enabled(self, dpid):
|
||||
'''Is node connected, admin on, powered on, and fault-free?
|
||||
|
||||
@param dpid dpid
|
||||
|
||||
@return bool node is enabled
|
||||
'''
|
||||
ni = self.node_info[dpid]
|
||||
return ni.connected and ni.admin_on and ni.power_on and not ni.fault
|
||||
|
||||
def nodes_enabled(self, dpids, enabled = True):
|
||||
'''Return subset of enabled nodes
|
||||
|
||||
@param dpids list of dpids
|
||||
@param enabled only return enabled nodes?
|
||||
|
||||
@return dpids filtered list of dpids
|
||||
'''
|
||||
if enabled:
|
||||
return [n for n in dpids if self.node_enabled(n)]
|
||||
def nodes(self, sort=True):
|
||||
"Return nodes in graph"
|
||||
if sort:
|
||||
return self.sorted( self.g.nodes() )
|
||||
else:
|
||||
return dpids
|
||||
return self.g.nodes()
|
||||
|
||||
def nodes(self, enabled = True):
|
||||
'''Return graph nodes.
|
||||
|
||||
@param enabled only return enabled nodes?
|
||||
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
return self.nodes_enabled(self.g.nodes(), enabled)
|
||||
|
||||
def nodes_str(self, dpids):
|
||||
'''Return string of custom-encoded nodes.
|
||||
|
||||
@param dpids list of dpids
|
||||
|
||||
@return str string
|
||||
'''
|
||||
return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
|
||||
|
||||
def is_switch(self, n):
|
||||
def isSwitch(self, n):
|
||||
'''Returns true if node is a switch.'''
|
||||
return self.node_info[n].is_switch
|
||||
info = self.node_info[n]
|
||||
return info and info.get('isSwitch', False)
|
||||
|
||||
def switches(self, enabled = True):
|
||||
def switches(self, sort=True):
|
||||
'''Return switches.
|
||||
|
||||
@param enabled only return enabled nodes?
|
||||
|
||||
sort: sort switches alphabetically
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
nodes = [n for n in self.g.nodes() if self.is_switch(n)]
|
||||
return self.nodes_enabled(nodes, enabled)
|
||||
return [n for n in self.nodes(sort) if self.isSwitch(n)]
|
||||
|
||||
def hosts(self, enabled = True):
|
||||
def hosts(self, sort=True):
|
||||
'''Return hosts.
|
||||
|
||||
@param enabled only return enabled nodes?
|
||||
|
||||
sort: sort hosts alphabetically
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
|
||||
|
||||
def is_host(n):
|
||||
'''Returns true if node is a host.'''
|
||||
return not self.node_info[n].is_switch
|
||||
|
||||
nodes = [n for n in self.g.nodes() if is_host(n)]
|
||||
return self.nodes_enabled(nodes, enabled)
|
||||
|
||||
def edge_enabled(self, edge):
|
||||
'''Is edge admin on, powered on, and fault-free?
|
||||
|
||||
@param edge (src, dst) dpid tuple
|
||||
|
||||
@return bool edge is enabled
|
||||
def links(self, sort=True):
|
||||
'''Return links.
|
||||
sort: sort links alphabetically
|
||||
@return links list of name pairs
|
||||
'''
|
||||
src, dst = edge
|
||||
src, dst = tuple(sorted([src, dst]))
|
||||
ei = self.edge_info[tuple(sorted([src, dst]))]
|
||||
return ei.admin_on and ei.power_on and not ei.fault
|
||||
|
||||
def edges_enabled(self, edges, enabled = True):
|
||||
'''Return subset of enabled edges
|
||||
|
||||
@param edges list of edges
|
||||
@param enabled only return enabled edges?
|
||||
|
||||
@return edges filtered list of edges
|
||||
'''
|
||||
if enabled:
|
||||
return [e for e in edges if self.edge_enabled(e)]
|
||||
if not sort:
|
||||
return self.g.edges()
|
||||
else:
|
||||
return edges
|
||||
|
||||
def edges(self, enabled = True):
|
||||
'''Return edges.
|
||||
|
||||
@param enabled only return enabled edges?
|
||||
|
||||
@return edges list of dpid pairs
|
||||
'''
|
||||
return self.edges_enabled(self.g.edges(), enabled)
|
||||
|
||||
def edges_str(self, dpid_pairs):
|
||||
'''Return string of custom-encoded node pairs.
|
||||
|
||||
@param dpid_pairs list of dpid pairs (src, dst)
|
||||
|
||||
@return str string
|
||||
'''
|
||||
edges = []
|
||||
for pair in dpid_pairs:
|
||||
src, dst = pair
|
||||
src = str(self.id_gen(dpid = src))
|
||||
dst = str(self.id_gen(dpid = dst))
|
||||
edges.append((src, dst))
|
||||
return edges
|
||||
links = [tuple(self.sorted(e)) for e in self.g.edges()]
|
||||
return sorted( links, key=naturalSeq )
|
||||
|
||||
def port(self, src, dst):
|
||||
'''Get port number.
|
||||
|
||||
@param src source switch DPID
|
||||
@param dst destination switch DPID
|
||||
@param src source switch name
|
||||
@param dst destination switch name
|
||||
@return tuple (src_port, dst_port):
|
||||
src_port: port on source switch leading to the destination switch
|
||||
dst_port: port on destination switch leading to the source switch
|
||||
'''
|
||||
if src in self.ports and dst in self.ports[src]:
|
||||
assert dst in self.ports and src in self.ports[dst]
|
||||
return (self.ports[src][dst], self.ports[dst][src])
|
||||
return self.ports[src][dst], self.ports[dst][src]
|
||||
|
||||
def enable_edges(self):
|
||||
'''Enable all edges in the network graph.
|
||||
def linkInfo( self, src, dst ):
|
||||
"Return link metadata"
|
||||
src, dst = self.sorted([src, dst])
|
||||
return self.link_info[(src, dst)]
|
||||
|
||||
Set admin on, power on, and fault off.
|
||||
'''
|
||||
for e in self.g.edges():
|
||||
src, dst = e
|
||||
ei = self.edge_info[tuple(sorted([src, dst]))]
|
||||
ei.admin_on = True
|
||||
ei.power_on = True
|
||||
ei.fault = False
|
||||
def setlinkInfo( self, src, dst, info ):
|
||||
"Set link metadata"
|
||||
src, dst = self.sorted([src, dst])
|
||||
self.link_info[(src, dst)] = info
|
||||
|
||||
def enable_nodes(self):
|
||||
'''Enable all nodes in the network graph.
|
||||
def nodeInfo( self, name ):
|
||||
"Return metadata (dict) for node"
|
||||
info = self.node_info[ name ]
|
||||
return info if info is not None else {}
|
||||
|
||||
Set connected on, admin on, power on, and fault off.
|
||||
'''
|
||||
for node in self.g.nodes():
|
||||
ni = self.node_info[node]
|
||||
ni.connected = True
|
||||
ni.admin_on = True
|
||||
ni.power_on = True
|
||||
ni.fault = False
|
||||
def setNodeInfo( self, name, info ):
|
||||
"Set metadata (dict) for node"
|
||||
self.node_info[ name ] = info
|
||||
|
||||
def enable_all(self):
|
||||
'''Enable all nodes and edges in the network graph.'''
|
||||
self.enable_nodes()
|
||||
self.enable_edges()
|
||||
|
||||
def name(self, dpid):
|
||||
'''Get string name of node ID.
|
||||
|
||||
@param dpid DPID of host or switch
|
||||
@return name_str string name with no dashes
|
||||
'''
|
||||
return self.id_gen(dpid = dpid).name_str()
|
||||
|
||||
def ip(self, dpid):
|
||||
'''Get IP dotted-decimal string of node ID.
|
||||
|
||||
@param dpid DPID of host or switch
|
||||
@return ip_str
|
||||
'''
|
||||
return self.id_gen(dpid = dpid).ip_str()
|
||||
@staticmethod
|
||||
def sorted( items ):
|
||||
"Items sorted in natural (i.e. alphabetical) order"
|
||||
return sorted(items, key=natural)
|
||||
|
||||
|
||||
class SingleSwitchTopo(Topo):
|
||||
'''Single switch connected to k hosts.'''
|
||||
|
||||
def __init__(self, k = 2, enable_all = True):
|
||||
'''Init.
|
||||
|
||||
@param k number of hosts
|
||||
@param enable_all enables all nodes and switches?
|
||||
'''
|
||||
super(SingleSwitchTopo, self).__init__()
|
||||
class SingleSwitchTopo( Topo ):
|
||||
"Single switch connected to k hosts."
|
||||
|
||||
def build( self, k=2, **opts ):
|
||||
"k: number of hosts"
|
||||
self.k = k
|
||||
|
||||
self.add_node(1, Node())
|
||||
hosts = range(2, k + 2)
|
||||
for h in hosts:
|
||||
self.add_node(h, Node(is_switch = False))
|
||||
self.add_edge(h, 1, Edge())
|
||||
|
||||
if enable_all:
|
||||
self.enable_all()
|
||||
switch = self.addSwitch( 's1' )
|
||||
for h in irange( 1, k ):
|
||||
host = self.addHost( 'h%s' % h )
|
||||
self.addLink( host, switch )
|
||||
|
||||
|
||||
class SingleSwitchReversedTopo(SingleSwitchTopo):
|
||||
'''Single switch connected to k hosts, with reversed ports.
|
||||
|
||||
The lowest-numbered host is connected to the highest-numbered port.
|
||||
|
||||
Useful to verify that Mininet properly handles custom port numberings.
|
||||
'''
|
||||
|
||||
def port(self, src, dst):
|
||||
'''Get port number.
|
||||
|
||||
@param src source switch DPID
|
||||
@param dst destination switch DPID
|
||||
@return tuple (src_port, dst_port):
|
||||
src_port: port on source switch leading to the destination switch
|
||||
dst_port: port on destination switch leading to the source switch
|
||||
'''
|
||||
if src == 1:
|
||||
if dst in range(2, self.k + 2):
|
||||
dst_index = dst - 2
|
||||
highest = self.k - 1
|
||||
return (highest - dst_index, 0)
|
||||
else:
|
||||
raise Exception('unexpected dst: %i' % dst)
|
||||
elif src in range(2, self.k + 2):
|
||||
if dst == 1:
|
||||
raise Exception('unexpected dst: %i' % dst)
|
||||
else:
|
||||
src_index = src - 2
|
||||
highest = self.k - 1
|
||||
return (0, highest - src_index)
|
||||
|
||||
|
||||
class LinearTopo(Topo):
|
||||
'''Linear topology of k switches, with one host per switch.'''
|
||||
|
||||
def __init__(self, k = 2, enable_all = True):
|
||||
'''Init.
|
||||
|
||||
@param k number of switches (and hosts too)
|
||||
@param enable_all enables all nodes and switches?
|
||||
'''
|
||||
super(LinearTopo, self).__init__()
|
||||
class SingleSwitchReversedTopo( Topo ):
|
||||
"""Single switch connected to k hosts, with reversed ports.
|
||||
The lowest-numbered host is connected to the highest-numbered port.
|
||||
Useful to verify that Mininet properly handles custom port numberings."""
|
||||
|
||||
def build( self, k=2 ):
|
||||
"k: number of hosts"
|
||||
self.k = k
|
||||
switch = self.addSwitch( 's1' )
|
||||
for h in irange( 1, k ):
|
||||
host = self.addHost( 'h%s' % h )
|
||||
self.addLink( host, switch,
|
||||
port1=0, port2=( k - h + 1 ) )
|
||||
|
||||
switches = range(1, k + 1)
|
||||
for s in switches:
|
||||
h = s + k
|
||||
self.add_node(s, Node())
|
||||
self.add_node(h, Node(is_switch = False))
|
||||
self.add_edge(s, h, Edge())
|
||||
for s in switches:
|
||||
if s != k:
|
||||
self.add_edge(s, s + 1, Edge())
|
||||
class LinearTopo( Topo ):
|
||||
"Linear topology of k switches, with n hosts per switch."
|
||||
|
||||
if enable_all:
|
||||
self.enable_all()
|
||||
def build( self, k=2, n=1, **opts):
|
||||
"""k: number of switches
|
||||
n: number of hosts per switch"""
|
||||
self.k = k
|
||||
self.n = n
|
||||
|
||||
if n == 1:
|
||||
genHostName = lambda i, j: 'h%s' % i
|
||||
else:
|
||||
genHostName = lambda i, j: 'h%ss%d' % ( j, i )
|
||||
|
||||
lastSwitch = None
|
||||
for i in irange( 1, k ):
|
||||
# Add switch
|
||||
switch = self.addSwitch( 's%s' % i )
|
||||
# Add hosts to switch
|
||||
for j in irange( 1, n ):
|
||||
host = self.addHost( genHostName( i, j ) )
|
||||
self.addLink( host, switch )
|
||||
# Connect switch to previous
|
||||
if lastSwitch:
|
||||
self.addLink( switch, lastSwitch )
|
||||
lastSwitch = switch
|
||||
|
||||
+45
-21
@@ -1,45 +1,69 @@
|
||||
"Library of potentially useful topologies for Mininet"
|
||||
|
||||
from mininet.topo import Topo, Node
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
|
||||
class TreeTopo( Topo ):
|
||||
"Topology for a tree network with a given depth and fanout."
|
||||
|
||||
def __init__( self, depth=1, fanout=2 ):
|
||||
super( TreeTopo, self ).__init__()
|
||||
# Numbering: h1..N, sN+1..M
|
||||
hostCount = fanout ** depth
|
||||
def build( self, depth=1, fanout=2 ):
|
||||
# Numbering: h1..N, s1..M
|
||||
self.hostNum = 1
|
||||
self.switchNum = hostCount + 1
|
||||
self.switchNum = 1
|
||||
# Build topology
|
||||
self.addTree( depth, fanout )
|
||||
# Consider all switches and hosts 'on'
|
||||
self.enable_all()
|
||||
|
||||
# It is OK that i is "unused" in the for loop.
|
||||
# pylint: disable-msg=W0612
|
||||
|
||||
def addTree( self, depth, fanout ):
|
||||
"""Add a subtree starting with node n.
|
||||
returns: last node added"""
|
||||
isSwitch = depth > 0
|
||||
if isSwitch:
|
||||
num = self.switchNum
|
||||
node = self.addSwitch( 's%s' % self.switchNum )
|
||||
self.switchNum += 1
|
||||
else:
|
||||
num = self.hostNum
|
||||
self.hostNum += 1
|
||||
self.add_node( num, Node( is_switch=isSwitch ) )
|
||||
if isSwitch:
|
||||
for i in range( 0, fanout ):
|
||||
for _ in range( fanout ):
|
||||
child = self.addTree( depth - 1, fanout )
|
||||
self.add_edge( num, child )
|
||||
return num
|
||||
self.addLink( node, child )
|
||||
else:
|
||||
node = self.addHost( 'h%s' % self.hostNum )
|
||||
self.hostNum += 1
|
||||
return node
|
||||
|
||||
# pylint: enable-msg=W0612
|
||||
|
||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||
"Convenience function for creating tree networks."
|
||||
topo = TreeTopo( depth, fanout )
|
||||
return Mininet( topo, **kwargs )
|
||||
|
||||
|
||||
class TorusTopo( Topo ):
|
||||
"""2-D Torus topology
|
||||
WARNING: this topology has LOOPS and WILL NOT WORK
|
||||
with the default controller or any Ethernet bridge
|
||||
without STP turned on! It can be used with STP, e.g.:
|
||||
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
|
||||
|
||||
def build( self, x, y ):
|
||||
if x < 3 or y < 3:
|
||||
raise Exception( 'Please use 3x3 or greater for compatibility '
|
||||
'with 2.1' )
|
||||
hosts, switches, dpid = {}, {}, 0
|
||||
# Create and wire interior
|
||||
for i in range( 0, x ):
|
||||
for j in range( 0, y ):
|
||||
loc = '%dx%d' % ( i + 1, j + 1 )
|
||||
# dpid cannot be zero for OVS
|
||||
dpid = ( i + 1 ) * 256 + ( j + 1 )
|
||||
switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%016x' % dpid )
|
||||
host = hosts[ i, j ] = self.addHost( 'h' + loc )
|
||||
self.addLink( host, switch )
|
||||
# Connect switches
|
||||
for i in range( 0, x ):
|
||||
for j in range( 0, y ):
|
||||
sw1 = switches[ i, j ]
|
||||
sw2 = switches[ i, ( j + 1 ) % y ]
|
||||
sw3 = switches[ ( i + 1 ) % x, j ]
|
||||
self.addLink( sw1, sw2 )
|
||||
self.addLink( sw1, sw3 )
|
||||
|
||||
|
||||
|
||||
|
||||
+364
-45
@@ -1,11 +1,15 @@
|
||||
"Utility functions for Mininet."
|
||||
|
||||
from time import sleep
|
||||
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
import select
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
from mininet.log import output, info, error, warn, debug
|
||||
|
||||
from mininet.log import error
|
||||
from time import sleep
|
||||
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
from select import poll, POLLIN, POLLHUP
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
import re
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import os
|
||||
|
||||
# Command execution support
|
||||
|
||||
@@ -22,7 +26,7 @@ def checkRun( cmd ):
|
||||
# pylint doesn't understand explicit type checking
|
||||
# pylint: disable-msg=E1103
|
||||
|
||||
def quietRun( *cmd ):
|
||||
def oldQuietRun( *cmd ):
|
||||
"""Run a command, routing stderr to stdout, and return the output.
|
||||
cmd: list of command params"""
|
||||
if len( cmd ) == 1:
|
||||
@@ -33,22 +37,87 @@ def quietRun( *cmd ):
|
||||
# We can't use Popen.communicate() because it uses
|
||||
# select(), which can't handle
|
||||
# high file descriptor numbers! poll() can, however.
|
||||
output = ''
|
||||
readable = select.poll()
|
||||
out = ''
|
||||
readable = poll()
|
||||
readable.register( popen.stdout )
|
||||
while True:
|
||||
while readable.poll():
|
||||
data = popen.stdout.read( 1024 )
|
||||
if len( data ) == 0:
|
||||
break
|
||||
output += data
|
||||
out += data
|
||||
popen.poll()
|
||||
if popen.returncode != None:
|
||||
if popen.returncode is not None:
|
||||
break
|
||||
return output
|
||||
return out
|
||||
|
||||
|
||||
# This is a bit complicated, but it enables us to
|
||||
# monitor command output as it is happening
|
||||
|
||||
def errRun( *cmd, **kwargs ):
|
||||
"""Run a command and return stdout, stderr and return code
|
||||
cmd: string or list of command and args
|
||||
stderr: STDOUT to merge stderr with stdout
|
||||
shell: run command using shell
|
||||
echo: monitor output to console"""
|
||||
# Allow passing in a list or a string
|
||||
if len( cmd ) == 1:
|
||||
cmd = cmd[ 0 ]
|
||||
if isinstance( cmd, str ):
|
||||
cmd = cmd.split( ' ' )
|
||||
cmd = [ str( arg ) for arg in cmd ]
|
||||
# By default we separate stderr, don't run in a shell, and don't echo
|
||||
stderr = kwargs.get( 'stderr', PIPE )
|
||||
shell = kwargs.get( 'shell', False )
|
||||
echo = kwargs.get( 'echo', False )
|
||||
if echo:
|
||||
# cmd goes to stderr, output goes to stdout
|
||||
info( cmd, '\n' )
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
|
||||
# We use poll() because select() doesn't work with large fd numbers,
|
||||
# and thus communicate() doesn't work either
|
||||
out, err = '', ''
|
||||
poller = poll()
|
||||
poller.register( popen.stdout, POLLIN )
|
||||
fdtofile = { popen.stdout.fileno(): popen.stdout }
|
||||
outDone, errDone = False, True
|
||||
if popen.stderr:
|
||||
fdtofile[ popen.stderr.fileno() ] = popen.stderr
|
||||
poller.register( popen.stderr, POLLIN )
|
||||
errDone = False
|
||||
while not outDone or not errDone:
|
||||
readable = poller.poll()
|
||||
for fd, _event in readable:
|
||||
f = fdtofile[ fd ]
|
||||
data = f.read( 1024 )
|
||||
if echo:
|
||||
output( data )
|
||||
if f == popen.stdout:
|
||||
out += data
|
||||
if data == '':
|
||||
outDone = True
|
||||
elif f == popen.stderr:
|
||||
err += data
|
||||
if data == '':
|
||||
errDone = True
|
||||
returncode = popen.wait()
|
||||
return out, err, returncode
|
||||
|
||||
def errFail( *cmd, **kwargs ):
|
||||
"Run a command using errRun and raise exception on nonzero exit"
|
||||
out, err, ret = errRun( *cmd, **kwargs )
|
||||
if ret:
|
||||
raise Exception( "errFail: %s failed with return code %s: %s"
|
||||
% ( cmd, ret, err ) )
|
||||
return out, err, ret
|
||||
|
||||
def quietRun( cmd, **kwargs ):
|
||||
"Run a command and return merged stdout and stderr"
|
||||
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
|
||||
|
||||
# pylint: enable-msg=E1103
|
||||
# pylint: disable-msg=E1101,W0612
|
||||
# pylint: disable-msg=E1101
|
||||
|
||||
def isShellBuiltin( cmd ):
|
||||
"Return True if cmd is a bash builtin."
|
||||
@@ -61,7 +130,7 @@ def isShellBuiltin( cmd ):
|
||||
|
||||
isShellBuiltin.builtIns = None
|
||||
|
||||
# pylint: enable-msg=E1101,W0612
|
||||
# pylint: enable-msg=E1101
|
||||
|
||||
# Interface management
|
||||
#
|
||||
@@ -86,7 +155,12 @@ def makeIntfPair( intf1, intf2 ):
|
||||
quietRun( 'ip link del ' + intf2 )
|
||||
# Create new pair
|
||||
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
|
||||
return quietRun( cmd )
|
||||
cmdOutput = quietRun( cmd )
|
||||
if cmdOutput == '':
|
||||
return True
|
||||
else:
|
||||
error( "Error creating interface pair: %s " % cmdOutput )
|
||||
return False
|
||||
|
||||
def retry( retries, delaySecs, fn, *args, **keywords ):
|
||||
"""Try something several times before giving up.
|
||||
@@ -102,47 +176,70 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
|
||||
error( "*** gave up after %i retries\n" % tries )
|
||||
exit( 1 )
|
||||
|
||||
def moveIntfNoRetry( intf, node, printError=False ):
|
||||
def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
|
||||
"""Move interface to node, without retrying.
|
||||
intf: string, interface
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
|
||||
quietRun( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not ( ' %s:' % intf ) in links:
|
||||
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, dstNode.pid )
|
||||
if srcNode:
|
||||
srcNode.cmd( cmd )
|
||||
else:
|
||||
quietRun( cmd )
|
||||
if ( ' %s:' % intf ) not in dstNode.cmd( 'ip link show', intf ):
|
||||
if printError:
|
||||
error( '*** Error: moveIntf: ' + intf +
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
' not successfully moved to ' + dstNode.name + '\n' )
|
||||
return False
|
||||
return True
|
||||
|
||||
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
|
||||
def moveIntf( intf, dstNode, srcNode=None, printError=False,
|
||||
retries=3, delaySecs=0.001 ):
|
||||
"""Move interface to node, retrying on failure.
|
||||
intf: string, interface
|
||||
node: Node object
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns
|
||||
printError: if true, print error"""
|
||||
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
|
||||
retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
|
||||
srcNode=srcNode, printError=printError )
|
||||
|
||||
def createLink( node1, node2, port1=None, port2=None ):
|
||||
"""Create a link between nodes, making an interface for each.
|
||||
node1: Node object
|
||||
node2: Node object
|
||||
port1: node1 port number (optional)
|
||||
port2: node2 port number (optional)
|
||||
returns: intf1 name, intf2 name"""
|
||||
return node1.linkTo( node2, port1, port2 )
|
||||
# Support for dumping network
|
||||
|
||||
def dumpNodeConnections( nodes ):
|
||||
"Dump connections to/from nodes."
|
||||
|
||||
def dumpConnections( node ):
|
||||
"Helper function: dump connections to node"
|
||||
for intf in node.intfList():
|
||||
output( ' %s:' % intf )
|
||||
if intf.link:
|
||||
intfs = [ intf.link.intf1, intf.link.intf2 ]
|
||||
intfs.remove( intf )
|
||||
output( intfs[ 0 ] )
|
||||
else:
|
||||
output( ' ' )
|
||||
|
||||
for node in nodes:
|
||||
output( node.name )
|
||||
dumpConnections( node )
|
||||
output( '\n' )
|
||||
|
||||
def dumpNetConnections( net ):
|
||||
"Dump connections in network"
|
||||
nodes = net.controllers + net.switches + net.hosts
|
||||
dumpNodeConnections( nodes )
|
||||
|
||||
# IP and Mac address formatting and parsing
|
||||
|
||||
def _colonHex( val, count ):
|
||||
def _colonHex( val, bytecount ):
|
||||
"""Generate colon-hex string.
|
||||
val: input as unsigned int
|
||||
count: number of bytes to convert
|
||||
bytecount: number of bytes to convert
|
||||
returns: chStr colon-hex string"""
|
||||
pieces = []
|
||||
for i in range( count - 1, -1, -1 ):
|
||||
for i in range( bytecount - 1, -1, -1 ):
|
||||
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
|
||||
pieces.append( '%02x' % piece )
|
||||
chStr = ':'.join( pieces )
|
||||
@@ -157,24 +254,50 @@ def macColonHex( mac ):
|
||||
def ipStr( ip ):
|
||||
"""Generate IP address string from an unsigned int.
|
||||
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
|
||||
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
|
||||
w = ( ip & 0xff000000 ) >> 24
|
||||
w = 10 if w == 0 else w
|
||||
x = ( ip & 0xff0000 ) >> 16
|
||||
y = ( ip & 0xff00 ) >> 8
|
||||
returns: ip address string w.x.y.z"""
|
||||
w = ( ip >> 24 ) & 0xff
|
||||
x = ( ip >> 16 ) & 0xff
|
||||
y = ( ip >> 8 ) & 0xff
|
||||
z = ip & 0xff
|
||||
return "%i.%i.%i.%i" % ( w, x, y, z )
|
||||
|
||||
def ipNum( w, x, y, z ):
|
||||
"""Generate unsigned int from components ofIP address
|
||||
"""Generate unsigned int from components of IP address
|
||||
returns: w << 24 | x << 16 | y << 8 | z"""
|
||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||
|
||||
def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||
"""Return IP address string from ints
|
||||
i: int to be added to ipbase
|
||||
prefixLen: optional IP prefix length
|
||||
ipBaseNum: option base IP address as int
|
||||
returns IP address as string"""
|
||||
imax = 0xffffffff >> prefixLen
|
||||
assert i <= imax, 'Not enough IP addresses in the subnet'
|
||||
mask = 0xffffffff ^ imax
|
||||
ipnum = ( ipBaseNum & mask ) + i
|
||||
return ipStr( ipnum )
|
||||
|
||||
def ipParse( ip ):
|
||||
"Parse an IP address and return an unsigned int."
|
||||
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
||||
while ( len(args) < 4 ):
|
||||
args.append( 0 )
|
||||
return ipNum( *args )
|
||||
|
||||
def netParse( ipstr ):
|
||||
"""Parse an IP network specification, returning
|
||||
address and prefix len as unsigned ints"""
|
||||
prefixLen = 0
|
||||
if '/' in ipstr:
|
||||
ip, pf = ipstr.split( '/' )
|
||||
prefixLen = int( pf )
|
||||
#if no prefix is specified, set the prefix to 24
|
||||
else:
|
||||
ip = ipstr
|
||||
prefixLen = 24
|
||||
return ipParse( ip ), prefixLen
|
||||
|
||||
def checkInt( s ):
|
||||
"Check if input string is an int"
|
||||
try:
|
||||
@@ -200,10 +323,206 @@ def makeNumeric( s ):
|
||||
else:
|
||||
return s
|
||||
|
||||
# Popen support
|
||||
|
||||
def pmonitor(popens, timeoutms=500, readline=True,
|
||||
readmax=1024 ):
|
||||
"""Monitor dict of hosts to popen objects
|
||||
a line at a time
|
||||
timeoutms: timeout for poll()
|
||||
readline: return single line of output
|
||||
yields: host, line/output (if any)
|
||||
terminates: when all EOFs received"""
|
||||
poller = poll()
|
||||
fdToHost = {}
|
||||
for host, popen in popens.iteritems():
|
||||
fd = popen.stdout.fileno()
|
||||
fdToHost[ fd ] = host
|
||||
poller.register( fd, POLLIN )
|
||||
if not readline:
|
||||
# Use non-blocking reads
|
||||
flags = fcntl( fd, F_GETFL )
|
||||
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
|
||||
while popens:
|
||||
fds = poller.poll( timeoutms )
|
||||
if fds:
|
||||
for fd, event in fds:
|
||||
host = fdToHost[ fd ]
|
||||
popen = popens[ host ]
|
||||
if event & POLLIN:
|
||||
if readline:
|
||||
# Attempt to read a line of output
|
||||
# This blocks until we receive a newline!
|
||||
line = popen.stdout.readline()
|
||||
else:
|
||||
line = popen.stdout.read( readmax )
|
||||
yield host, line
|
||||
# Check for EOF
|
||||
elif event & POLLHUP:
|
||||
poller.unregister( fd )
|
||||
del popens[ host ]
|
||||
else:
|
||||
yield None, ''
|
||||
|
||||
# Other stuff we use
|
||||
def sysctlTestAndSet( name, limit ):
|
||||
"Helper function to set sysctl limits"
|
||||
#convert non-directory names into directory names
|
||||
if '/' not in name:
|
||||
name = '/proc/sys/' + name.replace( '.', '/' )
|
||||
#read limit
|
||||
with open( name, 'r' ) as readFile:
|
||||
oldLimit = readFile.readline()
|
||||
if type( limit ) is int:
|
||||
#compare integer limits before overriding
|
||||
if int( oldLimit ) < limit:
|
||||
with open( name, 'w' ) as writeFile:
|
||||
writeFile.write( "%d" % limit )
|
||||
else:
|
||||
#overwrite non-integer limits
|
||||
with open( name, 'w' ) as writeFile:
|
||||
writeFile.write( limit )
|
||||
|
||||
def rlimitTestAndSet( name, limit ):
|
||||
"Helper function to set rlimits"
|
||||
soft, hard = getrlimit( name )
|
||||
if soft < limit:
|
||||
hardLimit = hard if limit < hard else limit
|
||||
setrlimit( name, ( limit, hardLimit ) )
|
||||
|
||||
def fixLimits():
|
||||
"Fix ridiculously small resource limits."
|
||||
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
|
||||
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
|
||||
debug( "*** Setting resource limits\n" )
|
||||
try:
|
||||
rlimitTestAndSet( RLIMIT_NPROC, 8192 )
|
||||
rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
|
||||
#Increase open file limit
|
||||
sysctlTestAndSet( 'fs.file-max', 10000 )
|
||||
#Increase network buffer space
|
||||
sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
|
||||
sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
|
||||
sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
|
||||
sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
|
||||
sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
|
||||
#Increase arp cache size
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
|
||||
#Increase routing table size
|
||||
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
|
||||
#Increase number of PTYs for nodes
|
||||
sysctlTestAndSet( 'kernel.pty.max', 20000 )
|
||||
except:
|
||||
warn( "*** Error setting resource limits. "
|
||||
"Mininet's performance may be affected.\n" )
|
||||
|
||||
def mountCgroups():
|
||||
"Make sure cgroups file system is mounted"
|
||||
mounts = quietRun( 'cat /proc/mounts' )
|
||||
cgdir = '/sys/fs/cgroup'
|
||||
csdir = cgdir + '/cpuset'
|
||||
if ('cgroup %s' % cgdir not in mounts and
|
||||
'cgroups %s' % cgdir not in mounts):
|
||||
raise Exception( "cgroups not mounted on " + cgdir )
|
||||
if 'cpuset %s' % csdir not in mounts:
|
||||
errRun( 'mkdir -p ' + csdir )
|
||||
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
|
||||
|
||||
def natural( text ):
|
||||
"To sort sanely/alphabetically: sorted( l, key=natural )"
|
||||
def num( s ):
|
||||
"Convert text segment to int if necessary"
|
||||
return int( s ) if s.isdigit() else s
|
||||
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
|
||||
|
||||
def naturalSeq( t ):
|
||||
"Natural sort key function for sequences"
|
||||
return [ natural( x ) for x in t ]
|
||||
|
||||
def numCores():
|
||||
"Returns number of CPU cores based on /proc/cpuinfo"
|
||||
if hasattr( numCores, 'ncores' ):
|
||||
return numCores.ncores
|
||||
try:
|
||||
numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
|
||||
except ValueError:
|
||||
return 0
|
||||
return numCores.ncores
|
||||
|
||||
def irange(start, end):
|
||||
"""Inclusive range from start to end (vs. Python insanity.)
|
||||
irange(1,5) -> 1, 2, 3, 4, 5"""
|
||||
return range( start, end + 1 )
|
||||
|
||||
def custom( cls, **params ):
|
||||
"Returns customized constructor for class cls."
|
||||
# Note: we may wish to see if we can use functools.partial() here
|
||||
# and in customConstructor
|
||||
def customized( *args, **kwargs):
|
||||
"Customized constructor"
|
||||
kwargs = kwargs.copy()
|
||||
kwargs.update( params )
|
||||
return cls( *args, **kwargs )
|
||||
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
|
||||
return customized
|
||||
|
||||
def splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
split = argstr.split( ',' )
|
||||
fn = split[ 0 ]
|
||||
params = split[ 1: ]
|
||||
# Convert int and float args; removes the need for function
|
||||
# to be flexible with input arg formats.
|
||||
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||
kwargs = {}
|
||||
for s in [ p for p in params if '=' in p ]:
|
||||
key, val = s.split( '=', 1 )
|
||||
kwargs[ key ] = makeNumeric( val )
|
||||
return fn, args, kwargs
|
||||
|
||||
def customConstructor( constructors, argStr ):
|
||||
"""Return custom constructor based on argStr
|
||||
The args and key/val pairs in argsStr will be automatically applied
|
||||
when the generated constructor is later used.
|
||||
"""
|
||||
cname, newargs, kwargs = splitArgs( argStr )
|
||||
constructor = constructors.get( cname, None )
|
||||
|
||||
if not constructor:
|
||||
raise Exception( "error: %s is unknown - please specify one of %s" %
|
||||
( cname, constructors.keys() ) )
|
||||
|
||||
def customized( name, *args, **params ):
|
||||
"Customized constructor, useful for Node, Link, and other classes"
|
||||
params = params.copy()
|
||||
params.update( kwargs )
|
||||
if not newargs:
|
||||
return constructor( name, *args, **params )
|
||||
if args:
|
||||
warn( 'warning: %s replacing %s with %s\n' % (
|
||||
constructor, args, newargs ) )
|
||||
return constructor( name, *newargs, **params )
|
||||
|
||||
customized.__name__ = 'customConstructor(%s)' % argStr
|
||||
return customized
|
||||
|
||||
def buildTopo( topos, topoStr ):
|
||||
"""Create topology from string with format (object, arg1, arg2,...).
|
||||
input topos is a dict of topo names to constructors, possibly w/args.
|
||||
"""
|
||||
topo, args, kwargs = splitArgs( topoStr )
|
||||
if topo not in topos:
|
||||
raise Exception( 'Invalid topo name %s' % topo )
|
||||
return topos[ topo ]( *args, **kwargs )
|
||||
|
||||
def ensureRoot():
|
||||
"""Ensure that we are running as root.
|
||||
|
||||
Probably we should only sudo when needed as per Big Switch's patch.
|
||||
"""
|
||||
if os.getuid() != 0:
|
||||
print "*** Mininet must run as root."
|
||||
exit( 1 )
|
||||
return
|
||||
|
||||
@@ -5,32 +5,102 @@
|
||||
*
|
||||
* - closing all file descriptors except stdin/out/error
|
||||
* - detaching from a controlling tty using setsid
|
||||
* - running in a network namespace
|
||||
* - running in network and mount namespaces
|
||||
* - printing out the pid of a process so we can identify it later
|
||||
* - attaching to a namespace and cgroup
|
||||
* - setting RT scheduling
|
||||
*
|
||||
* Partially based on public domain setsid(1)
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <linux/sched.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#if !defined(VERSION)
|
||||
#define VERSION "(devel)"
|
||||
#endif
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
printf("Execution utility for Mininet.\n"
|
||||
"usage: %s [-cdnp]\n"
|
||||
"-c: close all file descriptors except stdin/out/error\n"
|
||||
"-d: detach from tty by calling setsid()\n"
|
||||
"-n: run in new network namespace\n"
|
||||
"-p: print ^A + pid\n", name);
|
||||
printf("Execution utility for Mininet\n\n"
|
||||
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
|
||||
"Options:\n"
|
||||
" -c: close all file descriptors except stdin/out/error\n"
|
||||
" -d: detach from tty by calling setsid()\n"
|
||||
" -n: run in new network and mount namespaces\n"
|
||||
" -p: print ^A + pid\n"
|
||||
" -a pid: attach to pid's network and mount namespaces\n"
|
||||
" -g group: add to cgroup\n"
|
||||
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
|
||||
" -v: print version\n",
|
||||
name);
|
||||
}
|
||||
|
||||
|
||||
int setns(int fd, int nstype)
|
||||
{
|
||||
return syscall(__NR_setns, fd, nstype);
|
||||
}
|
||||
|
||||
/* Validate alphanumeric path foo1/bar2/baz */
|
||||
void validate(char *path)
|
||||
{
|
||||
char *s;
|
||||
for (s=path; *s; s++) {
|
||||
if (!isalnum(*s) && *s != '/') {
|
||||
fprintf(stderr, "invalid path: %s\n", path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add our pid to cgroup */
|
||||
void cgroup(char *gname)
|
||||
{
|
||||
static char path[PATH_MAX];
|
||||
static char *groups[] = {
|
||||
"cpu", "cpuacct", "cpuset", NULL
|
||||
};
|
||||
char **gptr;
|
||||
pid_t pid = getpid();
|
||||
int count = 0;
|
||||
validate(gname);
|
||||
for (gptr = groups; *gptr; gptr++) {
|
||||
FILE *f;
|
||||
snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks",
|
||||
*gptr, gname);
|
||||
f = fopen(path, "w");
|
||||
if (f) {
|
||||
count++;
|
||||
fprintf(f, "%d\n", pid);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
if (!count) {
|
||||
fprintf(stderr, "cgroup: could not add to cgroup %s\n",
|
||||
gname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
int fd;
|
||||
|
||||
while ((c = getopt(argc, argv, "+cdnp")) != -1)
|
||||
char path[PATH_MAX];
|
||||
int nsid;
|
||||
int pid;
|
||||
static struct sched_param sp;
|
||||
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
|
||||
switch(c) {
|
||||
case 'c':
|
||||
/* close file descriptors except stdin/out/error */
|
||||
@@ -44,36 +114,86 @@ int main(int argc, char *argv[])
|
||||
case -1:
|
||||
perror("fork");
|
||||
return 1;
|
||||
case 0: /* child */
|
||||
case 0: /* child */
|
||||
break;
|
||||
default: /* parent */
|
||||
default: /* parent */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
setsid();
|
||||
break;
|
||||
case 'n':
|
||||
/* run in network namespace */
|
||||
if (unshare(CLONE_NEWNET) == -1) {
|
||||
/* run in network and mount namespaces */
|
||||
if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
|
||||
perror("unshare");
|
||||
return 1;
|
||||
}
|
||||
/* mount sysfs to pick up the new network namespace */
|
||||
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
|
||||
perror("mount");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
/* print pid */
|
||||
printf("\001%d\n", getpid());
|
||||
fflush(stdout);
|
||||
break;
|
||||
case 'a':
|
||||
/* Attach to pid's network namespace and mount namespace */
|
||||
pid = atoi(optarg);
|
||||
sprintf(path, "/proc/%d/ns/net", pid);
|
||||
nsid = open(path, O_RDONLY);
|
||||
if (nsid < 0) {
|
||||
perror(path);
|
||||
return 1;
|
||||
}
|
||||
if (setns(nsid, 0) != 0) {
|
||||
perror("setns");
|
||||
return 1;
|
||||
}
|
||||
/* Plan A: call setns() to attach to mount namespace */
|
||||
sprintf(path, "/proc/%d/ns/mnt", pid);
|
||||
nsid = open(path, O_RDONLY);
|
||||
if (nsid < 0 || setns(nsid, 0) != 0) {
|
||||
/* Plan B: chroot into pid's root file system */
|
||||
sprintf(path, "/proc/%d/root", pid);
|
||||
if (chroot(path) < 0) {
|
||||
perror(path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
/* Attach to cgroup */
|
||||
cgroup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
/* Set RT scheduling priority */
|
||||
sp.sched_priority = atoi(optarg);
|
||||
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
|
||||
perror("sched_setscheduler");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
printf("%s\n", VERSION);
|
||||
exit(0);
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
exit(0);
|
||||
default:
|
||||
usage(argv[0]);
|
||||
break;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
perror(argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
perror(argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
usage(argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,33 +5,39 @@
|
||||
from setuptools import setup, find_packages
|
||||
from os.path import join
|
||||
|
||||
scripts = [ join( 'bin', filename ) for filename in [
|
||||
'mn', 'mnexec' ] ]
|
||||
# Get version number from source tree
|
||||
import sys
|
||||
sys.path.append( '.' )
|
||||
from mininet.net import VERSION
|
||||
|
||||
scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ]
|
||||
|
||||
modname = distname = 'mininet'
|
||||
|
||||
setup(
|
||||
name=distname,
|
||||
version='0.0.0',
|
||||
version=VERSION,
|
||||
description='Process-based OpenFlow emulator',
|
||||
author='Bob Lantz',
|
||||
author_email='rlantz@cs.stanford.edu',
|
||||
packages=find_packages(exclude='test'),
|
||||
packages=[ 'mininet', 'mininet.examples' ],
|
||||
long_description="""
|
||||
Insert longer description here.
|
||||
""",
|
||||
Mininet is a network emulator which uses lightweight
|
||||
virtualization to create virtual networks for rapid
|
||||
prototyping of Software-Defined Network (SDN) designs
|
||||
using OpenFlow. http://mininet.org
|
||||
""",
|
||||
classifiers=[
|
||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python",
|
||||
"Development Status :: 4 - Beta",
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Internet",
|
||||
"Topic :: System :: Emulators",
|
||||
],
|
||||
keywords='networking protocol Internet OpenFlow',
|
||||
license='unspecified',
|
||||
keywords='networking emulator protocol Internet OpenFlow SDN',
|
||||
license='BSD',
|
||||
install_requires=[
|
||||
'setuptools',
|
||||
'networkx'
|
||||
'setuptools'
|
||||
],
|
||||
scripts=scripts,
|
||||
)
|
||||
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Attempt to build debian packages for OVS
|
||||
|
||||
set -e # exit on error
|
||||
set -u # exit on undefined variable
|
||||
|
||||
kvers=`uname -r`
|
||||
ksrc=/lib/modules/$kvers/build
|
||||
dist=`lsb_release -is | tr [A-Z] [a-z]`
|
||||
release=`lsb_release -rs`
|
||||
arch=`uname -m`
|
||||
buildsuffix=-2
|
||||
if [ "$arch" = "i686" ]; then arch=i386; fi
|
||||
if [ "$arch" = "x86_64" ]; then arch=amd64; fi
|
||||
|
||||
overs=1.4.0
|
||||
ovs=openvswitch-$overs
|
||||
ovstgz=$ovs.tar.gz
|
||||
ovsurl=http://openvswitch.org/releases/$ovstgz
|
||||
|
||||
install='sudo apt-get install -y'
|
||||
|
||||
echo "*** Installing debian/ubuntu build system"
|
||||
$install build-essential devscripts ubuntu-dev-tools debhelper dh-make
|
||||
$install diff patch cdbs quilt gnupg fakeroot lintian pbuilder piuparts
|
||||
$install module-assistant
|
||||
|
||||
echo "*** Installing OVS dependencies"
|
||||
$install pkg-config gcc make python-dev libssl-dev libtool
|
||||
$install dkms ipsec-tools
|
||||
|
||||
echo "*** Installing headers for $kvers"
|
||||
$install linux-headers-$kvers
|
||||
|
||||
echo "*** Retrieving OVS source"
|
||||
wget -c $ovsurl
|
||||
tar xzf $ovstgz
|
||||
cd $ovs
|
||||
|
||||
echo "*** Patching OVS source"
|
||||
# Not sure why this fails, but off it goes!
|
||||
sed -i -e 's/dh_strip/# dh_strip/' debian/rules
|
||||
if [ "$release" = "10.04" ]; then
|
||||
# Lucid doesn't seem to have all the packages for ovsdbmonitor
|
||||
echo "*** Patching debian/rules to remove dh_python2"
|
||||
sed -i -e 's/dh_python2/dh_pysupport/' debian/rules
|
||||
echo "*** Not building ovsdbmonitor since it's too hard on 10.04"
|
||||
mv debian/ovsdbmonitor.install debian/ovsdbmonitor.install.backup
|
||||
sed -i -e 's/ovsdbmonitor.install/ovsdbmonitor.install.backup/' Makefile.in
|
||||
else
|
||||
# Install a bag of hurt for ovsdbmonitor
|
||||
$install python-pyside.qtcore pyqt4-dev-tools python-twisted python-twisted-bin \
|
||||
python-twisted-core python-twisted-conch python-anyjson python-zope.interface
|
||||
fi
|
||||
# init script was written to assume that commands complete
|
||||
sed -i -e 's/^set -e/#set -e/' debian/openvswitch-controller.init
|
||||
|
||||
echo "*** Building OVS user packages"
|
||||
opts=--with-linux=/lib/modules/`uname -r`/build
|
||||
fakeroot make -f debian/rules DATAPATH_CONFIGURE_OPTS=$opts binary
|
||||
|
||||
echo "*** Building OVS datapath kernel module package"
|
||||
# Still looking for the "right" way to do this...
|
||||
sudo mkdir -p /usr/src/linux
|
||||
ln -sf _debian/openvswitch.tar.gz .
|
||||
sudo make -f debian/rules.modules KSRC=$ksrc KVERS=$kvers binary-modules
|
||||
|
||||
echo "*** Built the following packages:"
|
||||
cd ~
|
||||
ls -l *deb
|
||||
|
||||
archive=ovs-$overs-core-$dist-$release-$arch$buildsuffix.tar
|
||||
ovsbase='common pki switch brcompat controller datapath-dkms'
|
||||
echo "*** Packing up $ovsbase .debs into:"
|
||||
echo " $archive"
|
||||
pkgs=""
|
||||
for component in $ovsbase; do
|
||||
if echo $component | egrep 'dkms|pki'; then
|
||||
# Architecture-independent packages
|
||||
deb=(openvswitch-${component}_$overs*all.deb)
|
||||
else
|
||||
deb=(openvswitch-${component}_$overs*$arch.deb)
|
||||
fi
|
||||
pkgs="$pkgs $deb"
|
||||
done
|
||||
rm -rf $archive
|
||||
tar cf $archive $pkgs
|
||||
|
||||
echo "*** Contents of archive $archive:"
|
||||
tar tf $archive
|
||||
|
||||
echo "*** Done (hopefully)"
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ if __name__ == '__main__':
|
||||
fixLines( infile.readlines(), outfid )
|
||||
infile.close()
|
||||
os.close( outfid )
|
||||
call( [ 'doxypy.py', outname ] )
|
||||
call( [ 'doxypy', outname ] )
|
||||
|
||||
|
||||
|
||||
|
||||
+487
-176
@@ -1,13 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mininet install script for Ubuntu (and Debian Lenny)
|
||||
# Brandon Heller (brandonh@stanford.edu)
|
||||
|
||||
# Fail on error
|
||||
set -e
|
||||
|
||||
|
||||
# Fail on unset var usage
|
||||
set -o nounset
|
||||
|
||||
# Get directory containing mininet folder
|
||||
MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )"
|
||||
|
||||
# Set up build directory, which by default is the working directory
|
||||
# unless the working directory is a subdirectory of mininet,
|
||||
# in which case we use the directory containing mininet
|
||||
BUILD_DIR="$(pwd -P)"
|
||||
case $BUILD_DIR in
|
||||
$MININET_DIR/*) BUILD_DIR=$MININET_DIR;; # currect directory is a subdirectory
|
||||
*) BUILD_DIR=$BUILD_DIR;;
|
||||
esac
|
||||
|
||||
# Location of CONFIG_NET_NS-enabled kernel(s)
|
||||
KERNEL_LOC=http://www.openflow.org/downloads/mininet
|
||||
|
||||
@@ -16,197 +29,365 @@ KERNEL_LOC=http://www.openflow.org/downloads/mininet
|
||||
DIST=Unknown
|
||||
RELEASE=Unknown
|
||||
CODENAME=Unknown
|
||||
ARCH=`uname -m`
|
||||
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi
|
||||
if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi
|
||||
|
||||
test -e /etc/debian_version && DIST="Debian"
|
||||
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
|
||||
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
|
||||
sudo apt-get install -y lsb-release
|
||||
install='sudo apt-get -y install'
|
||||
remove='sudo apt-get -y remove'
|
||||
pkginst='sudo dpkg -i'
|
||||
# Prereqs for this script
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install lsb-release
|
||||
fi
|
||||
fi
|
||||
test -e /etc/fedora-release && DIST="Fedora"
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
install='sudo yum -y install'
|
||||
remove='sudo yum -y erase'
|
||||
pkginst='sudo rpm -ivh'
|
||||
# Prereqs for this script
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install redhat-lsb-core
|
||||
fi
|
||||
fi
|
||||
if which lsb_release &> /dev/null; then
|
||||
DIST=`lsb_release -is`
|
||||
RELEASE=`lsb_release -rs`
|
||||
CODENAME=`lsb_release -cs`
|
||||
fi
|
||||
echo "Detected Linux distribution: $DIST $RELEASE $CODENAME"
|
||||
echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
|
||||
|
||||
# Kernel params
|
||||
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
KERNEL_NAME=2.6.33.1-mininet
|
||||
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
||||
KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
if [ "$RELEASE" = "10.04" ]; then
|
||||
KERNEL_NAME='3.0.0-15-generic'
|
||||
else
|
||||
KERNEL_NAME=`uname -r`
|
||||
fi
|
||||
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}
|
||||
else
|
||||
echo "Install.sh currently only supports Ubuntu and Debian."
|
||||
KERNEL_NAME=`uname -r`
|
||||
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
|
||||
|
||||
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora'; then
|
||||
echo "Install.sh currently only supports Ubuntu, Debian and Fedora."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# More distribution info
|
||||
DIST_LC=`echo $DIST | tr [A-Z] [a-z]` # as lower case
|
||||
|
||||
# Kernel Deb pkg to be removed:
|
||||
KERNEL_IMAGE_OLD=linux-image-2.6.26-2-686
|
||||
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
|
||||
|
||||
DRIVERS_DIR=/lib/modules/${KERNEL_NAME}/kernel/drivers/net
|
||||
|
||||
OVS_RELEASE=v1.2.2
|
||||
OVS_SRC=~/openvswitch
|
||||
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
|
||||
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
|
||||
OVS_RELEASE=1.4.0
|
||||
OVS_PACKAGE_LOC=https://github.com/downloads/mininet/mininet
|
||||
OVS_BUILDSUFFIX=-ignore # was -2
|
||||
OVS_PACKAGE_NAME=ovs-$OVS_RELEASE-core-$DIST_LC-$RELEASE-$ARCH$OVS_BUILDSUFFIX.tar
|
||||
OVS_TAG=v$OVS_RELEASE
|
||||
|
||||
# Command-line versions overrides that simplify custom VM creation
|
||||
# To use, pass in the vars on the cmd line before install.sh, e.g.
|
||||
# WS_DISSECTOR_REV=pre-ws-1.10.0 install.sh -w
|
||||
WS_DISSECTOR_REV=${WS_DISSECTOR_REV:-""}
|
||||
OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
|
||||
|
||||
|
||||
function kernel {
|
||||
echo "Install Mininet-compatible kernel if necessary"
|
||||
sudo apt-get update
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
# The easy approach: download pre-built linux-image and linux-headers packages:
|
||||
wget -c $KERNEL_LOC/$KERNEL_HEADERS
|
||||
wget -c $KERNEL_LOC/$KERNEL_IMAGE
|
||||
|
||||
# Install custom linux headers and image:
|
||||
sudo dpkg -i $KERNEL_IMAGE $KERNEL_HEADERS
|
||||
|
||||
# The next two steps are to work around a bug in newer versions of
|
||||
# kernel-package, which fails to add initrd images with the latest kernels.
|
||||
# See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=525032
|
||||
# Generate initrd image if the .deb didn't install it:
|
||||
if ! test -e /boot/initrd.img-${KERNEL_NAME}; then
|
||||
sudo update-initramfs -c -k ${KERNEL_NAME}
|
||||
fi
|
||||
|
||||
# Ensure /boot/grub/menu.lst boots with initrd image:
|
||||
sudo update-grub
|
||||
|
||||
# The default should be the new kernel. Otherwise, you may need to modify
|
||||
# /boot/grub/menu.lst to set the default to the entry corresponding to the
|
||||
# kernel you just installed.
|
||||
fi
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||
sudo apt-get -y install linux-image-$KERNEL_NAME
|
||||
fi
|
||||
$install linux-image-$KERNEL_NAME
|
||||
}
|
||||
|
||||
function kernel_clean {
|
||||
echo "Cleaning kernel..."
|
||||
|
||||
# To save disk space, remove previous kernel
|
||||
sudo apt-get -y remove $KERNEL_IMAGE_OLD
|
||||
if ! $remove $KERNEL_IMAGE_OLD; then
|
||||
echo $KERNEL_IMAGE_OLD not installed.
|
||||
fi
|
||||
|
||||
# Also remove downloaded packages:
|
||||
rm -f ~/linux-headers-* ~/linux-image-*
|
||||
rm -f $HOME/linux-headers-* $HOME/linux-image-*
|
||||
}
|
||||
|
||||
# Install Mininet deps
|
||||
function mn_deps {
|
||||
echo "Installing Mininet dependencies"
|
||||
sudo aptitude install -y gcc make screen psmisc xterm ssh iperf iproute \
|
||||
python-setuptools python-networkx
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||
echo "Upgrading networkx to avoid deprecation warning"
|
||||
sudo easy_install --upgrade networkx
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install gcc make socat psmisc xterm openssh-clients iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man pyflakes pylint python-pep8
|
||||
else
|
||||
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
|
||||
python-setuptools cgroup-bin ethtool help2man \
|
||||
pyflakes pylint pep8
|
||||
fi
|
||||
|
||||
# Add sysctl parameters as noted in the INSTALL file to increase kernel
|
||||
# limits to support larger setups:
|
||||
sudo su -c "cat $HOME/mininet/util/sysctl_addon >> /etc/sysctl.conf"
|
||||
|
||||
# Load new sysctl settings:
|
||||
sudo sysctl -p
|
||||
|
||||
echo "Installing Mininet core"
|
||||
pushd ~/mininet
|
||||
pushd $MININET_DIR/mininet
|
||||
sudo make install
|
||||
popd
|
||||
}
|
||||
|
||||
# Install Mininet developer dependencies
|
||||
function mn_dev {
|
||||
echo "Installing Mininet developer dependencies"
|
||||
$install doxygen doxypy texlive-fonts-recommended
|
||||
}
|
||||
|
||||
# The following will cause a full OF install, covering:
|
||||
# -user switch
|
||||
# -dissector
|
||||
# The instructions below are an abbreviated version from
|
||||
# http://www.openflowswitch.org/wk/index.php/Debian_Install
|
||||
# ... modified to use Debian Lenny rather than unstable.
|
||||
function of {
|
||||
echo "Installing OpenFlow and its tools..."
|
||||
|
||||
cd ~/
|
||||
sudo apt-get install -y git-core automake m4 pkg-config libtool \
|
||||
make libc6-dev autoconf autotools-dev gcc
|
||||
echo "Installing OpenFlow reference implementation..."
|
||||
cd $BUILD_DIR
|
||||
$install autoconf automake libtool make gcc
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install git pkgconfig glibc-devel
|
||||
else
|
||||
$install git-core autotools-dev pkg-config libc6-dev
|
||||
fi
|
||||
git clone git://openflowswitch.org/openflow.git
|
||||
cd ~/openflow
|
||||
cd $BUILD_DIR/openflow
|
||||
|
||||
# Patch controller to handle more than 16 switches
|
||||
patch -p1 < ~/mininet/util/openflow-patches/controller.patch
|
||||
patch -p1 < $MININET_DIR/mininet/util/openflow-patches/controller.patch
|
||||
|
||||
# Resume the install:
|
||||
./boot.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
# Install dissector:
|
||||
sudo apt-get install -y wireshark libgtk2.0-dev
|
||||
cd ~/openflow/utilities/wireshark_dissectors/openflow
|
||||
make
|
||||
sudo make install
|
||||
|
||||
# Copy coloring rules: OF is white-on-blue:
|
||||
mkdir -p ~/.wireshark
|
||||
cp ~/mininet/util/colorfilters ~/.wireshark
|
||||
|
||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||
# sent during tests, near link status changes:
|
||||
sudo apt-get remove -y avahi-daemon
|
||||
|
||||
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||
if [ "$DIST" = "Ubuntu" ]; then
|
||||
BLACKLIST=/etc/modprobe.d/blacklist.conf
|
||||
else
|
||||
BLACKLIST=/etc/modprobe.d/blacklist
|
||||
fi
|
||||
sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||
cd $BUILD_DIR
|
||||
}
|
||||
|
||||
function of13 {
|
||||
echo "Installing OpenFlow 1.3 soft switch implementation..."
|
||||
cd $BUILD_DIR/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
|
||||
unzip libpcre3-dev flex bison libboost-dev
|
||||
|
||||
if [ ! -d "ofsoftswitch13" ]; then
|
||||
git clone https://github.com/CPqD/ofsoftswitch13.git
|
||||
if [[ -n "$OF13_SWITCH_REV" ]]; then
|
||||
cd ofsoftswitch13
|
||||
git checkout ${OF13_SWITCH_REV}
|
||||
cd ..
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install netbee
|
||||
NBEESRC="nbeesrc-jan-10-2013"
|
||||
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
|
||||
wget -nc ${NBEEURL}${NBEESRC}.zip
|
||||
unzip ${NBEESRC}.zip
|
||||
cd ${NBEESRC}/src
|
||||
cmake .
|
||||
make
|
||||
cd $BUILD_DIR/
|
||||
sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib
|
||||
sudo ldconfig
|
||||
sudo cp -R ${NBEESRC}/include/ /usr/
|
||||
|
||||
# Resume the install:
|
||||
cd $BUILD_DIR/ofsoftswitch13
|
||||
./boot.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd $BUILD_DIR
|
||||
}
|
||||
|
||||
function wireshark_version_check {
|
||||
# Check Wireshark version
|
||||
WS=$(which wireshark)
|
||||
WS_VER_PATCH=(1 10) # targetting wireshark 1.10.0
|
||||
WS_VER=($($WS --version | sed 's/[a-z ]*\([0-9]*\).\([0-9]*\).\([0-9]*\).*/\1 \2 \3/'))
|
||||
if [ "${WS_VER[0]}" -lt "${WS_VER_PATCH[0]}" ] ||
|
||||
[[ "${WS_VER[0]}" -le "${WS_VER_PATCH[0]}" && "${WS_VER[1]}" -lt "${WS_VER_PATCH[1]}" ]]
|
||||
then
|
||||
# pre-1.10.0 wireshark
|
||||
echo "Setting revision: pre-ws-1.10.0"
|
||||
WS_DISSECTOR_REV="pre-ws-1.10.0"
|
||||
fi
|
||||
}
|
||||
|
||||
function wireshark {
|
||||
echo "Installing Wireshark dissector..."
|
||||
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
# Just install Fedora's wireshark RPMS
|
||||
# Fedora's wirehark >= 1.10.2-2 includes an OF dissector
|
||||
# (it has been backported from the future Wireshark 1.12 code base)
|
||||
$install wireshark wireshark-gnome
|
||||
return
|
||||
fi
|
||||
|
||||
$install wireshark tshark libgtk2.0-dev
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" != "10.04" ]; then
|
||||
# Install newer version
|
||||
$install scons mercurial libglib2.0-dev
|
||||
$install libwiretap-dev libwireshark-dev
|
||||
cd $BUILD_DIR
|
||||
hg clone https://bitbucket.org/barnstorm/of-dissector
|
||||
if [[ -z "$WS_DISSECTOR_REV" ]]; then
|
||||
wireshark_version_check
|
||||
fi
|
||||
cd of-dissector
|
||||
if [[ -n "$WS_DISSECTOR_REV" ]]; then
|
||||
hg checkout ${WS_DISSECTOR_REV}
|
||||
fi
|
||||
# Build dissector
|
||||
cd src
|
||||
export WIRESHARK=/usr/include/wireshark
|
||||
scons
|
||||
# libwireshark0/ on 11.04; libwireshark1/ on later
|
||||
WSDIR=`find /usr/lib -type d -name 'libwireshark*' | head -1`
|
||||
WSPLUGDIR=$WSDIR/plugins/
|
||||
sudo cp openflow.so $WSPLUGDIR
|
||||
echo "Copied openflow plugin to $WSPLUGDIR"
|
||||
else
|
||||
# Install older version from reference source
|
||||
cd $BUILD_DIR/openflow/utilities/wireshark_dissectors/openflow
|
||||
make
|
||||
sudo make install
|
||||
fi
|
||||
|
||||
# Copy coloring rules: OF is white-on-blue:
|
||||
mkdir -p $HOME/.wireshark
|
||||
cp $MININET_DIR/mininet/util/colorfilters $HOME/.wireshark
|
||||
}
|
||||
|
||||
|
||||
# Install Open vSwitch specific version Ubuntu package
|
||||
function ubuntuOvs {
|
||||
echo "Creating and Installing Open vSwitch packages..."
|
||||
|
||||
OVS_SRC=$BUILD_DIR/openvswitch
|
||||
OVS_TARBALL_LOC=http://openvswitch.org/releases
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
|
||||
rm -rf $OVS_SRC
|
||||
mkdir -p $OVS_SRC
|
||||
cd $OVS_SRC
|
||||
|
||||
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
|
||||
tar xzf openvswitch-$OVS_RELEASE.tar.gz
|
||||
else
|
||||
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
|
||||
# Remove any old packages
|
||||
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
|
||||
openvswitch-pki openvswitch-switch
|
||||
|
||||
# Get build deps
|
||||
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
|
||||
pkg-config bzip2 openssl python-all procps python-qt4 \
|
||||
python-zopeinterface python-twisted-conch dkms
|
||||
|
||||
# Build OVS
|
||||
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
|
||||
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
|
||||
cd ..
|
||||
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
|
||||
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
|
||||
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
|
||||
echo "Ignoring error installing openvswitch-controller"
|
||||
fi
|
||||
|
||||
modinfo openvswitch
|
||||
sudo ovs-vsctl show
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
else
|
||||
echo "Failed to install Open vSwitch. OS must be Ubuntu >= 12.04"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Install Open vSwitch
|
||||
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
|
||||
|
||||
function ovs {
|
||||
echo "Installing Open vSwitch..."
|
||||
|
||||
if [ "$DIST" = "Debian" ] && [ "$CODENAME" == "lenny" ]; then
|
||||
sudo aptitude -y install pkg-config gcc make git-core python-dev libssl-dev
|
||||
# Install Autoconf 2.63+ backport from Debian Backports repo:
|
||||
# Instructions from http://backports.org/dokuwiki/doku.php?id=instructions
|
||||
sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list"
|
||||
sudo apt-get update
|
||||
sudo apt-get -y --force-yes install debian-backports-keyring
|
||||
sudo apt-get -y --force-yes -t lenny-backports install autoconf
|
||||
if [ "$DIST" == "Fedora" ]; then
|
||||
$install openvswitch openvswitch-controller
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ]; then
|
||||
sudo apt-get -y install $KERNEL_HEADERS
|
||||
# Manually installing openvswitch-datapath may be necessary
|
||||
# for manually built kernel .debs using Debian's defective kernel
|
||||
# packaging, which doesn't yield usable headers.
|
||||
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||
# If you've already installed a datapath, assume you
|
||||
# know what you're doing and don't need dkms datapath.
|
||||
# Otherwise, install it.
|
||||
$install openvswitch-datapath-dkms
|
||||
fi
|
||||
|
||||
# Install OVS from release
|
||||
cd ~/
|
||||
git clone git://openvswitch.org/openvswitch
|
||||
cd $OVS_SRC
|
||||
git checkout $OVS_RELEASE
|
||||
./boot.sh
|
||||
BUILDDIR=/lib/modules/${KERNEL_NAME}/build
|
||||
if [ ! -e $BUILDDIR ]; then
|
||||
echo "Creating build sdirectory $BUILDDIR"
|
||||
sudo mkdir -p $BUILDDIR
|
||||
$install openvswitch-switch openvswitch-controller
|
||||
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
opts="--with-linux=$BUILDDIR"
|
||||
mkdir -p $OVS_BUILD
|
||||
cd $OVS_BUILD
|
||||
../configure $opts
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
}
|
||||
|
||||
function remove_ovs {
|
||||
pkgs=`dpkg --get-selections | grep openvswitch | awk '{ print $1;}'`
|
||||
echo "Removing existing Open vSwitch packages:"
|
||||
echo $pkgs
|
||||
if ! $remove $pkgs; then
|
||||
echo "Not all packages removed correctly"
|
||||
fi
|
||||
# For some reason this doesn't happen
|
||||
if scripts=`ls /etc/init.d/*openvswitch* 2>/dev/null`; then
|
||||
echo $scripts
|
||||
for s in $scripts; do
|
||||
s=$(basename $s)
|
||||
echo SCRIPT $s
|
||||
sudo service $s stop
|
||||
sudo rm -f /etc/init.d/$s
|
||||
sudo update-rc.d -f $s remove
|
||||
done
|
||||
fi
|
||||
echo "Done removing OVS"
|
||||
}
|
||||
|
||||
function ivs {
|
||||
echo "Installing Indigo Virtual Switch..."
|
||||
|
||||
IVS_SRC=$BUILD_DIR/ivs
|
||||
|
||||
# Install dependencies
|
||||
$install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
|
||||
|
||||
# Install IVS from source
|
||||
cd $BUILD_DIR
|
||||
git clone git://github.com/floodlight/ivs $IVS_SRC --recursive
|
||||
cd $IVS_SRC
|
||||
make
|
||||
sudo make install
|
||||
# openflowd is deprecated, but for now copy it in
|
||||
sudo cp tests/test-openflowd /usr/local/bin/ovs-openflowd
|
||||
}
|
||||
|
||||
# Install NOX with tutorial files
|
||||
@@ -214,27 +395,32 @@ function nox {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
|
||||
# Install NOX deps:
|
||||
sudo apt-get -y install autoconf automake g++ libtool python python-twisted \
|
||||
$install autoconf automake g++ libtool python python-twisted \
|
||||
swig libssl-dev make
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
sudo apt-get -y install libboost1.35-dev
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
sudo apt-get -y install python-dev libboost-dev
|
||||
sudo apt-get -y install libboost-filesystem-dev
|
||||
sudo apt-get -y install libboost-test-dev
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
# Install NOX optional deps:
|
||||
sudo apt-get install -y libsqlite3-dev python-simplejson
|
||||
$install libsqlite3-dev python-simplejson
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd ~/
|
||||
git clone git://noxrepo.org/nox noxcore
|
||||
cd $BUILD_DIR/
|
||||
git clone https://github.com/noxrepo/nox-classic.git noxcore
|
||||
cd noxcore
|
||||
git checkout -b destiny remotes/origin/destiny
|
||||
if ! git checkout -b destiny remotes/origin/destiny ; then
|
||||
echo "Did not check out a new destiny branch - assuming current branch is destiny"
|
||||
fi
|
||||
|
||||
# Apply patches
|
||||
git checkout -b tutorial-destiny
|
||||
git am ~/mininet/util/nox-patches/*.patch
|
||||
git am $MININET_DIR/mininet/util/nox-patches/*tutorial-port-nox-destiny*.patch
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
|
||||
git am $MININET_DIR/mininet/util/nox-patches/*nox-ubuntu12-hacks.patch
|
||||
fi
|
||||
|
||||
# Build
|
||||
./boot.sh
|
||||
@@ -245,58 +431,131 @@ function nox {
|
||||
#make check
|
||||
|
||||
# Add NOX_CORE_DIR env var:
|
||||
sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=~/noxcore/build/src|' ~/.bashrc
|
||||
sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=$BUILD_DIR/noxcore/build/src|' ~/.bashrc
|
||||
|
||||
# To verify this install:
|
||||
#cd ~/noxcore/build/src
|
||||
#./nox_core -v -i ptcp:
|
||||
}
|
||||
|
||||
# Install NOX Classic/Zaku for OpenFlow 1.3
|
||||
function nox13 {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
|
||||
# Install NOX deps:
|
||||
$install autoconf automake g++ libtool python python-twisted \
|
||||
swig libssl-dev make
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd $BUILD_DIR/
|
||||
git clone https://github.com/CPqD/nox13oflib.git
|
||||
cd nox13oflib
|
||||
|
||||
# Build
|
||||
./boot.sh
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
make -j3
|
||||
#make check
|
||||
|
||||
# To verify this install:
|
||||
#cd ~/nox13oflib/build/src
|
||||
#./nox_core -v -i ptcp:
|
||||
}
|
||||
|
||||
|
||||
# "Install" POX
|
||||
function pox {
|
||||
echo "Installing POX into $BUILD_DIR/pox..."
|
||||
cd $BUILD_DIR
|
||||
git clone https://github.com/noxrepo/pox.git
|
||||
}
|
||||
|
||||
# Install OFtest
|
||||
function oftest {
|
||||
echo "Installing oftest..."
|
||||
|
||||
# Install deps:
|
||||
sudo apt-get install -y tcpdump python-scapy
|
||||
$install tcpdump python-scapy
|
||||
|
||||
# Install oftest:
|
||||
cd ~/
|
||||
git clone git://openflow.org/oftest
|
||||
cd oftest
|
||||
cd tools/munger
|
||||
sudo make install
|
||||
cd $BUILD_DIR/
|
||||
git clone git://github.com/floodlight/oftest
|
||||
}
|
||||
|
||||
# Install cbench
|
||||
function cbench {
|
||||
echo "Installing cbench..."
|
||||
|
||||
sudo apt-get install -y libsnmp-dev libpcap-dev
|
||||
cd ~/
|
||||
git clone git://openflow.org/oflops.git
|
||||
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install net-snmp-devel libpcap-devel libconfig-devel
|
||||
else
|
||||
$install libsnmp-dev libpcap-dev libconfig-dev
|
||||
fi
|
||||
cd $BUILD_DIR/
|
||||
git clone git://gitosis.stanford.edu/oflops.git
|
||||
cd oflops
|
||||
sh boot.sh || true # possible error in autoreconf, so run twice
|
||||
sh boot.sh
|
||||
./configure --with-openflow-src-dir=$HOME/openflow
|
||||
./configure --with-openflow-src-dir=$BUILD_DIR/openflow
|
||||
make
|
||||
sudo make install || true # make install fails; force past this
|
||||
}
|
||||
|
||||
function other {
|
||||
echo "Doing other setup tasks..."
|
||||
function vm_other {
|
||||
echo "Doing other Mininet VM setup tasks..."
|
||||
|
||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||
# sent during tests, near link status changes:
|
||||
echo "Removing avahi-daemon"
|
||||
$remove avahi-daemon
|
||||
|
||||
# was: Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||
#echo "Attempting to disable IPv6"
|
||||
#if [ "$DIST" = "Ubuntu" ]; then
|
||||
# BLACKLIST=/etc/modprobe.d/blacklist.conf
|
||||
#else
|
||||
# BLACKLIST=/etc/modprobe.d/blacklist
|
||||
#fi
|
||||
#sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||
|
||||
# Disable IPv6
|
||||
if ! grep 'disable IPv6' /etc/sysctl.conf; then
|
||||
echo 'Disabling IPv6'
|
||||
echo '
|
||||
# Mininet: disable IPv6
|
||||
net.ipv6.conf.all.disable_ipv6 = 1
|
||||
net.ipv6.conf.default.disable_ipv6 = 1
|
||||
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
fi
|
||||
# Disabling IPv6 breaks X11 forwarding via ssh
|
||||
line='AddressFamily inet'
|
||||
file='/etc/ssh/sshd_config'
|
||||
echo "Adding $line to $file"
|
||||
if ! grep "$line" $file > /dev/null; then
|
||||
echo "$line" | sudo tee -a $file > /dev/null
|
||||
fi
|
||||
|
||||
# Enable command auto completion using sudo; modify ~/.bashrc:
|
||||
sed -i -e 's|# for examples$|&\ncomplete -cf sudo|' ~/.bashrc
|
||||
|
||||
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
|
||||
# Install tcpdump, cmd-line packet dump tool. Also install gitk,
|
||||
# a graphical git history viewer.
|
||||
sudo apt-get install -y tcpdump tshark gitk
|
||||
$install tcpdump gitk
|
||||
|
||||
# Install common text editors
|
||||
sudo apt-get install -y vim nano emacs
|
||||
$install vim nano emacs
|
||||
|
||||
# Install NTP
|
||||
sudo apt-get install -y ntp
|
||||
$install ntp
|
||||
|
||||
# Set git to colorize everything.
|
||||
git config --global color.diff auto
|
||||
@@ -321,23 +580,41 @@ function other {
|
||||
# a good idea to use a symbolic link in place of the copy below.
|
||||
function modprobe {
|
||||
echo "Setting up modprobe for OVS kmod..."
|
||||
|
||||
sudo cp $OVS_KMODS $DRIVERS_DIR
|
||||
sudo depmod -a ${KERNEL_NAME}
|
||||
set +o nounset
|
||||
if [ -z "$OVS_KMODS" ]; then
|
||||
echo "OVS_KMODS not set. Aborting."
|
||||
else
|
||||
sudo cp $OVS_KMODS $DRIVERS_DIR
|
||||
sudo depmod -a ${KERNEL_NAME}
|
||||
fi
|
||||
set -o nounset
|
||||
}
|
||||
|
||||
function all {
|
||||
echo "Running all commands..."
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
printf "\nFedora 18+ support (still work in progress):\n"
|
||||
printf " * Fedora 18+ has kernel 3.10 RPMS in the updates repositories\n"
|
||||
printf " * Fedora 18+ has openvswitch 1.10 RPMS in the updates repositories\n"
|
||||
printf " * the install.sh script options [-bfnpvw] should work.\n"
|
||||
printf " * for a basic setup just try:\n"
|
||||
printf " install.sh -fnpv\n\n"
|
||||
exit 3
|
||||
fi
|
||||
echo "Installing all packages except for -eix (doxypy, ivs, nox-classic)..."
|
||||
kernel
|
||||
mn_deps
|
||||
# Skip mn_dev (doxypy/texlive/fonts/etc.) because it's huge
|
||||
# mn_dev
|
||||
of
|
||||
wireshark
|
||||
ovs
|
||||
modprobe
|
||||
nox
|
||||
# We may add ivs once it's more mature
|
||||
# ivs
|
||||
# NOX-classic is deprecated, but you can install it manually if desired.
|
||||
# nox
|
||||
pox
|
||||
oftest
|
||||
cbench
|
||||
other
|
||||
echo "Please reboot, then run ./mininet/util/install.sh -c to remove unneeded packages."
|
||||
echo "Enjoy Mininet!"
|
||||
}
|
||||
|
||||
@@ -345,6 +622,7 @@ function all {
|
||||
function vm_clean {
|
||||
echo "Cleaning VM..."
|
||||
sudo apt-get clean
|
||||
sudo apt-get autoremove
|
||||
sudo rm -rf /tmp/*
|
||||
sudo rm -rf openvswitch*.tar.gz
|
||||
|
||||
@@ -365,16 +643,20 @@ function vm_clean {
|
||||
git config --global user.name "None"
|
||||
git config --global user.email "None"
|
||||
|
||||
# Remove mininet install script
|
||||
rm -f install-mininet.sh
|
||||
# Note: you can shrink the .vmdk in vmware using
|
||||
# vmware-vdiskmanager -k *.vmdk
|
||||
echo "Zeroing out disk blocks for efficient compaction..."
|
||||
time sudo dd if=/dev/zero of=/tmp/zero bs=1M
|
||||
sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero
|
||||
|
||||
}
|
||||
|
||||
function usage {
|
||||
printf 'Usage: %s [-acdfhkmntvxy]\n\n' $(basename $0) >&2
|
||||
|
||||
printf '\nUsage: %s [-abcdfhikmnprtvVwx03]\n\n' $(basename $0) >&2
|
||||
|
||||
printf 'This install script attempts to install useful packages\n' >&2
|
||||
printf 'for Mininet. It should (hopefully) work on Ubuntu 10.04, 11.10\n' >&2
|
||||
printf 'and Debian 5.0 (Lenny). If you run into trouble, try\n' >&2
|
||||
printf 'for Mininet. It should (hopefully) work on Ubuntu 11.10+\n' >&2
|
||||
printf 'If you run into trouble, try\n' >&2
|
||||
printf 'installing one thing at a time, and looking at the \n' >&2
|
||||
printf 'specific installation function in this script.\n\n' >&2
|
||||
|
||||
@@ -382,39 +664,68 @@ function usage {
|
||||
printf -- ' -a: (default) install (A)ll packages - good luck!\n' >&2
|
||||
printf -- ' -b: install controller (B)enchmark (oflops)\n' >&2
|
||||
printf -- ' -c: (C)lean up after kernel install\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -f: install open(F)low\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -e: install Mininet d(E)veloper dependencies\n' >&2
|
||||
printf -- ' -f: install Open(F)low\n' >&2
|
||||
printf -- ' -h: print this (H)elp message\n' >&2
|
||||
printf -- ' -i: install (I)ndigo Virtual Switch\n' >&2
|
||||
printf -- ' -k: install new (K)ernel\n' >&2
|
||||
printf -- ' -m: install Open vSwitch kernel (M)odule\n' >&2
|
||||
printf -- ' -n: install mini(N)et dependencies + core files\n' >&2
|
||||
printf -- ' -t: install o(T)her stuff\n' >&2
|
||||
printf -- ' -v: install open (V)switch\n' >&2
|
||||
printf -- ' -x: install NO(X) OpenFlow controller\n' >&2
|
||||
printf -- ' -y: install (A)ll packages\n' >&2
|
||||
|
||||
printf -- ' -m: install Open vSwitch kernel (M)odule from source dir\n' >&2
|
||||
printf -- ' -n: install Mini(N)et dependencies + core files\n' >&2
|
||||
printf -- ' -p: install (P)OX OpenFlow Controller\n' >&2
|
||||
printf -- ' -r: remove existing Open vSwitch packages\n' >&2
|
||||
printf -- ' -s <dir>: place dependency (S)ource/build trees in <dir>\n' >&2
|
||||
printf -- ' -t: complete o(T)her Mininet VM setup tasks\n' >&2
|
||||
printf -- ' -v: install Open (V)switch\n' >&2
|
||||
printf -- ' -V <version>: install a particular version of Open (V)switch on Ubuntu\n' >&2
|
||||
printf -- ' -w: install OpenFlow (W)ireshark dissector\n' >&2
|
||||
printf -- ' -x: install NO(X) Classic OpenFlow controller\n' >&2
|
||||
printf -- ' -0: (default) -0[fx] installs OpenFlow 1.0 versions\n' >&2
|
||||
printf -- ' -3: -3[fx] installs OpenFlow 1.3 versions\n' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
OF_VERSION=1.0
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
all
|
||||
else
|
||||
while getopts 'abcdfhkmntvx' OPTION
|
||||
while getopts 'abcdefhikmnprs:tvV:wx03' OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
a) all;;
|
||||
b) cbench;;
|
||||
c) kernel_clean;;
|
||||
d) vm_clean;;
|
||||
f) of;;
|
||||
e) mn_dev;;
|
||||
f) case $OF_VERSION in
|
||||
1.0) of;;
|
||||
1.3) of13;;
|
||||
*) echo "Invalid OpenFlow version $OF_VERSION";;
|
||||
esac;;
|
||||
h) usage;;
|
||||
i) ivs;;
|
||||
k) kernel;;
|
||||
m) modprobe;;
|
||||
n) mn_deps;;
|
||||
t) other;;
|
||||
p) pox;;
|
||||
r) remove_ovs;;
|
||||
s) mkdir -p $OPTARG; # ensure the directory is created
|
||||
BUILD_DIR="$( cd -P "$OPTARG" && pwd )"; # get the full path
|
||||
echo "Dependency installation directory: $BUILD_DIR";;
|
||||
t) vm_other;;
|
||||
v) ovs;;
|
||||
x) nox;;
|
||||
V) OVS_RELEASE=$OPTARG;
|
||||
ubuntuOvs;;
|
||||
w) wireshark;;
|
||||
x) case $OF_VERSION in
|
||||
1.0) nox;;
|
||||
1.3) nox13;;
|
||||
*) echo "Invalid OpenFlow version $OF_VERSION";;
|
||||
esac;;
|
||||
0) OF_VERSION=1.0;;
|
||||
3) OF_VERSION=1.3;;
|
||||
?) usage;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Attach to a Mininet host and run a command
|
||||
|
||||
if [ -z $1 ]; then
|
||||
echo "usage: $0 host cmd [args...]"
|
||||
exit 1
|
||||
else
|
||||
host=$1
|
||||
fi
|
||||
|
||||
pid=`ps ax | grep "mininet:$host$" | grep bash | awk '{print $1};'`
|
||||
|
||||
if echo $pid | grep -q ' '; then
|
||||
echo "Error: found multiple mininet:$host processes"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ "$pid" == "" ]; then
|
||||
echo "Could not find Mininet host $host"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [ -z $2 ]; then
|
||||
cmd='bash'
|
||||
else
|
||||
shift
|
||||
cmd=$*
|
||||
fi
|
||||
|
||||
cgroup=/sys/fs/cgroup/cpu/$host
|
||||
if [ -d "$cgroup" ]; then
|
||||
cg="-g $host"
|
||||
fi
|
||||
|
||||
# Check whether host should be running in a chroot dir
|
||||
rootdir="/var/run/mn/$host/root"
|
||||
if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
|
||||
cmd="'cd `pwd`; exec $cmd'"
|
||||
cmd="chroot $rootdir /bin/bash -c $cmd"
|
||||
fi
|
||||
|
||||
cmd="exec sudo mnexec -a $pid $cg $cmd"
|
||||
eval $cmd
|
||||
@@ -0,0 +1,175 @@
|
||||
From 166693d7cb640d4a41251b87e92c52d9c688196b Mon Sep 17 00:00:00 2001
|
||||
From: Bob Lantz <rlantz@cs.stanford.edu>
|
||||
Date: Mon, 14 May 2012 15:30:44 -0700
|
||||
Subject: [PATCH] Hacks to get NOX classic/destiny to compile under Ubuntu
|
||||
12.04
|
||||
|
||||
Thanks to Srinivasu R. Kanduru for the initial patch.
|
||||
|
||||
Apologies for the hacks - it is my hope that this will be fixed
|
||||
upstream eventually.
|
||||
|
||||
---
|
||||
config/ac_pkg_swig.m4 | 7 ++++---
|
||||
src/Make.vars | 2 +-
|
||||
src/nox/coreapps/pyrt/deferredcallback.cc | 2 +-
|
||||
src/nox/coreapps/pyrt/pyglue.cc | 2 +-
|
||||
src/nox/coreapps/pyrt/pyrt.cc | 2 +-
|
||||
src/nox/netapps/authenticator/auth.i | 2 ++
|
||||
src/nox/netapps/authenticator/flow_util.i | 1 +
|
||||
src/nox/netapps/routing/routing.i | 2 ++
|
||||
.../switch_management/pyswitch_management.i | 2 ++
|
||||
src/nox/netapps/tests/tests.cc | 2 +-
|
||||
src/nox/netapps/topology/pytopology.i | 2 ++
|
||||
11 files changed, 18 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/config/ac_pkg_swig.m4 b/config/ac_pkg_swig.m4
|
||||
index d12556e..9b608f2 100644
|
||||
--- a/config/ac_pkg_swig.m4
|
||||
+++ b/config/ac_pkg_swig.m4
|
||||
@@ -78,9 +78,10 @@ AC_DEFUN([AC_PROG_SWIG],[
|
||||
if test -z "$available_patch" ; then
|
||||
[available_patch=0]
|
||||
fi
|
||||
- if test $available_major -ne $required_major \
|
||||
- -o $available_minor -ne $required_minor \
|
||||
- -o $available_patch -lt $required_patch ; then
|
||||
+ major_done=`test $available_major -gt $required_major`
|
||||
+ minor_done=`test $available_minor -gt $required_minor`
|
||||
+ if test !$major_done -a !$minor_done \
|
||||
+ -a $available_patch -lt $required_patch ; then
|
||||
AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org])
|
||||
SWIG=''
|
||||
else
|
||||
diff --git a/src/Make.vars b/src/Make.vars
|
||||
index d70d6aa..93b2879 100644
|
||||
--- a/src/Make.vars
|
||||
+++ b/src/Make.vars
|
||||
@@ -53,7 +53,7 @@ AM_LDFLAGS += -export-dynamic
|
||||
endif
|
||||
|
||||
# set python runtimefiles to be installed in the same directory as pkg
|
||||
-pkglib_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||
+pkgdata_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||
BUILT_SOURCES = $(NOX_PYBUILDFILES)
|
||||
|
||||
# Runtime-files build and clean rules
|
||||
diff --git a/src/nox/coreapps/pyrt/deferredcallback.cc b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
index 3a40fa7..111a586 100644
|
||||
--- a/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
+++ b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
@@ -69,7 +69,7 @@ DeferredCallback::get_instance(const Callback& c)
|
||||
DeferredCallback* cb = new DeferredCallback(c);
|
||||
|
||||
// flag as used in *_wrap.cc....correct?
|
||||
- return SWIG_Python_NewPointerObj(cb, s, SWIG_POINTER_OWN | 0);
|
||||
+ return SWIG_Python_NewPointerObj(m, cb, s, SWIG_POINTER_OWN | 0);
|
||||
}
|
||||
|
||||
bool
|
||||
diff --git a/src/nox/coreapps/pyrt/pyglue.cc b/src/nox/coreapps/pyrt/pyglue.cc
|
||||
index 48b9716..317fd04 100644
|
||||
--- a/src/nox/coreapps/pyrt/pyglue.cc
|
||||
+++ b/src/nox/coreapps/pyrt/pyglue.cc
|
||||
@@ -874,7 +874,7 @@ to_python(const Flow& flow)
|
||||
if (!s) {
|
||||
throw std::runtime_error("Could not find Flow SWIG type_info");
|
||||
}
|
||||
- return SWIG_Python_NewPointerObj(f, s, SWIG_POINTER_OWN | 0);
|
||||
+ return SWIG_Python_NewPointerObj(m, f, s, SWIG_POINTER_OWN | 0);
|
||||
|
||||
// PyObject* dict = PyDict_New();
|
||||
// if (!dict) {
|
||||
diff --git a/src/nox/coreapps/pyrt/pyrt.cc b/src/nox/coreapps/pyrt/pyrt.cc
|
||||
index fbda461..8ec05d6 100644
|
||||
--- a/src/nox/coreapps/pyrt/pyrt.cc
|
||||
+++ b/src/nox/coreapps/pyrt/pyrt.cc
|
||||
@@ -776,7 +776,7 @@ Python_event_manager::create_python_context(const Context* ctxt,
|
||||
pretty_print_python_exception());
|
||||
}
|
||||
|
||||
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||
Py_INCREF(pyctxt); // XXX needed?
|
||||
|
||||
//Py_DECREF(m);
|
||||
diff --git a/src/nox/netapps/authenticator/auth.i b/src/nox/netapps/authenticator/auth.i
|
||||
index 1de1a17..bfa04e2 100644
|
||||
--- a/src/nox/netapps/authenticator/auth.i
|
||||
+++ b/src/nox/netapps/authenticator/auth.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.authenticator.pyauth"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "core_events.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
diff --git a/src/nox/netapps/authenticator/flow_util.i b/src/nox/netapps/authenticator/flow_util.i
|
||||
index f67c3ef..2a314e2 100644
|
||||
--- a/src/nox/netapps/authenticator/flow_util.i
|
||||
+++ b/src/nox/netapps/authenticator/flow_util.i
|
||||
@@ -32,6 +32,7 @@ using namespace vigil::applications;
|
||||
%}
|
||||
|
||||
%include "common-defs.i"
|
||||
+%include "std_list.i"
|
||||
|
||||
%import "netinet/netinet.i"
|
||||
%import "pyrt/event.i"
|
||||
diff --git a/src/nox/netapps/routing/routing.i b/src/nox/netapps/routing/routing.i
|
||||
index 44ccb3d..f9221a2 100644
|
||||
--- a/src/nox/netapps/routing/routing.i
|
||||
+++ b/src/nox/netapps/routing/routing.i
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
%module "nox.netapps.routing.pyrouting"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "pyrouting.hh"
|
||||
#include "routing.hh"
|
||||
diff --git a/src/nox/netapps/switch_management/pyswitch_management.i b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
index 72bfed4..ad2c90d 100644
|
||||
--- a/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
+++ b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.pyswitch_management"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "switch_management_proxy.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
diff --git a/src/nox/netapps/tests/tests.cc b/src/nox/netapps/tests/tests.cc
|
||||
index 20e900d..f027028 100644
|
||||
--- a/src/nox/netapps/tests/tests.cc
|
||||
+++ b/src/nox/netapps/tests/tests.cc
|
||||
@@ -306,7 +306,7 @@ private:
|
||||
throw runtime_error("Could not find PyContext SWIG type_info.");
|
||||
}
|
||||
|
||||
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||
assert(pyctxt);
|
||||
|
||||
Py_DECREF(m);
|
||||
diff --git a/src/nox/netapps/topology/pytopology.i b/src/nox/netapps/topology/pytopology.i
|
||||
index 94a9f4b..7a8cd94 100644
|
||||
--- a/src/nox/netapps/topology/pytopology.i
|
||||
+++ b/src/nox/netapps/topology/pytopology.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.topology"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "pytopology.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
--
|
||||
1.7.5.4
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||
0001: This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||
0002: This patch hacks nox-destiny to compile on Ubuntu 12.04.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user