Merge pull request #307 from coreemu/enhancement/distributed-flask
Updated Distributed
This commit is contained in:
commit
74e15d9c9d
48 changed files with 1674 additions and 2694 deletions
414
daemon/Pipfile.lock
generated
414
daemon/Pipfile.lock
generated
|
@ -14,6 +14,67 @@
|
|||
]
|
||||
},
|
||||
"default": {
|
||||
"asn1crypto": {
|
||||
"hashes": [
|
||||
"sha256:0b199f211ae690df3db4fd6c1c4ff976497fb1da689193e368eedbadc53d9292",
|
||||
"sha256:bca90060bd995c3f62c4433168eab407e44bdbdb567b3f3a396a676c1a4c4a3f"
|
||||
],
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"bcrypt": {
|
||||
"hashes": [
|
||||
"sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89",
|
||||
"sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42",
|
||||
"sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294",
|
||||
"sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161",
|
||||
"sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31",
|
||||
"sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5",
|
||||
"sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c",
|
||||
"sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0",
|
||||
"sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de",
|
||||
"sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e",
|
||||
"sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052",
|
||||
"sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09",
|
||||
"sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105",
|
||||
"sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133",
|
||||
"sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7",
|
||||
"sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc"
|
||||
],
|
||||
"version": "==3.1.7"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774",
|
||||
"sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d",
|
||||
"sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90",
|
||||
"sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b",
|
||||
"sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63",
|
||||
"sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45",
|
||||
"sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25",
|
||||
"sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3",
|
||||
"sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b",
|
||||
"sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647",
|
||||
"sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016",
|
||||
"sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4",
|
||||
"sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb",
|
||||
"sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753",
|
||||
"sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7",
|
||||
"sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9",
|
||||
"sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f",
|
||||
"sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8",
|
||||
"sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f",
|
||||
"sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc",
|
||||
"sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42",
|
||||
"sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3",
|
||||
"sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909",
|
||||
"sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45",
|
||||
"sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d",
|
||||
"sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512",
|
||||
"sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff",
|
||||
"sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201"
|
||||
],
|
||||
"version": "==1.12.3"
|
||||
},
|
||||
"configparser": {
|
||||
"hashes": [
|
||||
"sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c",
|
||||
|
@ -25,14 +86,33 @@
|
|||
"editable": true,
|
||||
"path": "."
|
||||
},
|
||||
"enum34": {
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850",
|
||||
"sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a",
|
||||
"sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79",
|
||||
"sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"
|
||||
"sha256:24b61e5fcb506424d3ec4e18bca995833839bf13c59fc43e530e488f28d46b8c",
|
||||
"sha256:25dd1581a183e9e7a806fe0543f485103232f940fcfc301db65e630512cce643",
|
||||
"sha256:3452bba7c21c69f2df772762be0066c7ed5dc65df494a1d53a58b683a83e1216",
|
||||
"sha256:41a0be220dd1ed9e998f5891948306eb8c812b512dc398e5a01846d855050799",
|
||||
"sha256:5751d8a11b956fbfa314f6553d186b94aa70fdb03d8a4d4f1c82dcacf0cbe28a",
|
||||
"sha256:5f61c7d749048fa6e3322258b4263463bfccefecb0dd731b6561cb617a1d9bb9",
|
||||
"sha256:72e24c521fa2106f19623a3851e9f89ddfdeb9ac63871c7643790f872a305dfc",
|
||||
"sha256:7b97ae6ef5cba2e3bb14256625423413d5ce8d1abb91d4f29b6d1a081da765f8",
|
||||
"sha256:961e886d8a3590fd2c723cf07be14e2a91cf53c25f02435c04d39e90780e3b53",
|
||||
"sha256:96d8473848e984184b6728e2c9d391482008646276c3ff084a1bd89e15ff53a1",
|
||||
"sha256:ae536da50c7ad1e002c3eee101871d93abdc90d9c5f651818450a0d3af718609",
|
||||
"sha256:b0db0cecf396033abb4a93c95d1602f268b3a68bb0a9cc06a7cff587bb9a7292",
|
||||
"sha256:cfee9164954c186b191b91d4193989ca994703b2fff406f71cf454a2d3c7327e",
|
||||
"sha256:e6347742ac8f35ded4a46ff835c60e68c22a536a8ae5c4422966d06946b6d4c6",
|
||||
"sha256:f27d93f0139a3c056172ebb5d4f9056e770fdf0206c2f422ff2ebbad142e09ed",
|
||||
"sha256:f57b76e46a58b63d1c6375017f4564a28f19a5ca912691fd2e4261b3414b618d"
|
||||
],
|
||||
"version": "==1.1.6"
|
||||
"version": "==2.7"
|
||||
},
|
||||
"fabric": {
|
||||
"hashes": [
|
||||
"sha256:160331934ea60036604928e792fa8e9f813266b098ef5562aa82b88527740389",
|
||||
"sha256:24842d7d51556adcabd885ac3cf5e1df73fc622a1708bf3667bf5927576cdfa6"
|
||||
],
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
|
@ -42,40 +122,48 @@
|
|||
},
|
||||
"grpcio": {
|
||||
"hashes": [
|
||||
"sha256:1303578092f1f6e4bfbc354c04ac422856c393723d3ffa032fff0f7cb5cfd693",
|
||||
"sha256:229c6b313cd82bec8f979b059d87f03cc1a48939b543fe170b5a9c5cf6a6bc69",
|
||||
"sha256:3cd3d99a8b5568d0d186f9520c16121a0f2a4bcad8e2b9884b76fb88a85a7774",
|
||||
"sha256:41cfb222db358227521f9638a6fbc397f310042a4db5539a19dea01547c621cd",
|
||||
"sha256:43330501660f636fd6547d1e196e395cd1e2c2ae57d62219d6184a668ffebda0",
|
||||
"sha256:45d7a2bd8b4f25a013296683f4140d636cdbb507d94a382ea5029a21e76b1648",
|
||||
"sha256:47dc935658a13b25108823dabd010194ddea9610357c5c1ef1ad7b3f5157ebee",
|
||||
"sha256:480aa7e2b56238badce0b9413a96d5b4c90c3bfbd79eba5a0501e92328d9669e",
|
||||
"sha256:4a0934c8b0f97e1d8c18e76c45afc0d02d33ab03125258179f2ac6c7a13f3626",
|
||||
"sha256:5624dab19e950f99e560400c59d87b685809e4cfcb2c724103f1ab14c06071f7",
|
||||
"sha256:60515b1405bb3dadc55e6ca99429072dad3e736afcf5048db5452df5572231ff",
|
||||
"sha256:610f97ebae742a57d336a69b09a9c7d7de1f62aa54aaa8adc635b38f55ba4382",
|
||||
"sha256:64ea189b2b0859d1f7b411a09185028744d494ef09029630200cc892e366f169",
|
||||
"sha256:686090c6c1e09e4f49585b8508d0a31d58bc3895e4049ea55b197d1381e9f70f",
|
||||
"sha256:7745c365195bb0605e3d47b480a2a4d1baa8a41a5fd0a20de5fa48900e2c886a",
|
||||
"sha256:79491e0d2b77a1c438116bf9e5f9e2e04e78b78524615e2ce453eff62db59a09",
|
||||
"sha256:825177dd4c601c487836b7d6b4ba268db59787157911c623ba59a7c03c8d3adc",
|
||||
"sha256:8a060e1f72fb94eee8a035ed29f1201ce903ad14cbe27bda56b4a22a8abda045",
|
||||
"sha256:90168cc6353e2766e47b650c963f21cfff294654b10b3a14c67e26a4e3683634",
|
||||
"sha256:94b7742734bceeff6d8db5edb31ac844cb68fc7f13617eca859ff1b78bb20ba1",
|
||||
"sha256:962aebf2dd01bbb2cdb64580e61760f1afc470781f9ecd5fe8f3d8dcd8cf4556",
|
||||
"sha256:9c8d9eacdce840b72eee7924c752c31b675f8aec74790e08cff184a4ea8aa9c1",
|
||||
"sha256:af5b929debc336f6bab9b0da6915f9ee5e41444012aed6a79a3c7e80d7662fdf",
|
||||
"sha256:b9cdb87fc77e9a3eabdc42a512368538d648fa0760ad30cf97788076985c790a",
|
||||
"sha256:c5e6380b90b389454669dc67d0a39fb4dc166416e01308fcddd694236b8329ef",
|
||||
"sha256:d60c90fe2bfbee735397bf75a2f2c4e70c5deab51cd40c6e4fa98fae018c8db6",
|
||||
"sha256:d8582c8b1b1063249da1588854251d8a91df1e210a328aeb0ece39da2b2b763b",
|
||||
"sha256:ddbf86ba3aa0ad8fed2867910d2913ee237d55920b55f1d619049b3399f04efc",
|
||||
"sha256:e46bc0664c5c8a0545857aa7a096289f8db148e7f9cca2d0b760113e8994bddc",
|
||||
"sha256:f6437f70ec7fed0ca3a0eef1146591bb754b418bb6c6b21db74f0333d624e135",
|
||||
"sha256:f71693c3396530c6b00773b029ea85e59272557e9bd6077195a6593e4229892a",
|
||||
"sha256:f79f7455f8fbd43e8e9d61914ecf7f48ba1c8e271801996fef8d6a8f3cc9f39f"
|
||||
"sha256:0302331e014fc4bac028b6ad480b33f7abfe20b9bdcca7be417124dda8f22115",
|
||||
"sha256:0aa0cce9c5eb1261b32173a20ed42b51308d55ce28ecc2021e868b3cb90d9503",
|
||||
"sha256:0c83947575300499adbc308e986d754e7f629be0bdd9bea1ffdd5cf76e1f1eff",
|
||||
"sha256:0ca26ff968d45efd4ef73447c4d4b34322ea8c7d06fbb6907ce9e5db78f1bbcb",
|
||||
"sha256:0cf80a7955760c2498f8821880242bb657d70998065ff0d2a082de5ffce230a7",
|
||||
"sha256:0d40706e57d9833fe0e023a08b468f33940e8909affa12547874216d36bba208",
|
||||
"sha256:11872069156de34c6f3f9a1deb46cc88bc35dfde88262c4c73eb22b39b16fc55",
|
||||
"sha256:16065227faae0ab0abf1789bfb92a2cd2ab5da87630663f93f8178026da40e0d",
|
||||
"sha256:1e33778277685f6fabb22539136269c87c029e39b6321ef1a639b756a1c0a408",
|
||||
"sha256:2b16be15b1ae656bc7a36642b8c7045be2dde2048bb4b67478003e9d9db8022a",
|
||||
"sha256:3701dfca3ada27ceef0d17f728ce9dfef155ed20c57979c2b05083082258c6c1",
|
||||
"sha256:41912ecaf482abf2de74c69f509878f99223f5dd6b2de1a09c955afd4de3cf9b",
|
||||
"sha256:4332cbd20544fe7406910137590f38b5b3a1f6170258e038652cf478c639430f",
|
||||
"sha256:44068ecbdc6467c2bff4d8198816c8a2701b6dd1ec16078fceb6adc7c1f577d6",
|
||||
"sha256:53115960e37059420e2d16a4b04b00dd2ab3b6c3c67babd01ffbfdcd7881a69b",
|
||||
"sha256:6e7027bcd4070414751e2a5e60706facb98a1fc636497c9bac5442fe37b8ae6b",
|
||||
"sha256:6ff57fb2f07b7226b5bec89e8e921ea9bd220f35f11e094f2ba38f09eecd49c6",
|
||||
"sha256:73240e244d7644654bbda1f309f4911748b6a1804b7a8897ddbe8a04c90f7407",
|
||||
"sha256:785234bbc469bc75e26c868789a2080ffb30bd6e93930167797729889ad06b0b",
|
||||
"sha256:82f9d3c7f91d2d1885631335c003c5d45ae1cd69cc0bc4893f21fef50b8151bc",
|
||||
"sha256:86bdc2a965510658407a1372eb61f0c92f763fdfb2795e4d038944da4320c950",
|
||||
"sha256:95e925b56676a55e6282b3de80a1cbad5774072159779c61eac02791dface049",
|
||||
"sha256:96673bb4f14bd3263613526d1e7e33fdb38a9130e3ce87bf52314965706e1900",
|
||||
"sha256:970014205e76920484679035b6fb4b16e02fc977e5aac4d22025da849c79dab9",
|
||||
"sha256:ace5e8bf11a1571f855f5dab38a9bd34109b6c9bc2864abf24a597598c7e3695",
|
||||
"sha256:ad375f03eb3b9cb75a24d91eab8609e134d34605f199efc41e20dd642bdac855",
|
||||
"sha256:b819c4c7dcf0de76788ce5f95daad6d4e753d6da2b6a5f84e5bb5b5ce95fddc4",
|
||||
"sha256:c17943fd340cbd906db49f3f03c7545e5a66b617e8348b2c7a0d2c759d216af1",
|
||||
"sha256:d21247150dea86dabd3b628d8bc4b563036db3d332b3f4db3c5b1b0b122cb4f6",
|
||||
"sha256:d4d500a7221116de9767229ff5dd10db91f789448d85befb0adf5a37b0cd83b5",
|
||||
"sha256:e2a942a3cfccbbca21a90c144867112698ef36486345c285da9e98c466f22b22",
|
||||
"sha256:e983273dca91cb8a5043bc88322eb48e2b8d4e4998ff441a1ee79ced89db3909"
|
||||
],
|
||||
"version": "==1.23.0"
|
||||
"version": "==1.24.1"
|
||||
},
|
||||
"invoke": {
|
||||
"hashes": [
|
||||
"sha256:c52274d2e8a6d64ef0d61093e1983268ea1fc0cd13facb9448c4ef0c9a7ac7da",
|
||||
"sha256:f4ec8a134c0122ea042c8912529f87652445d9f4de590b353d23f95bfa1f0efd",
|
||||
"sha256:fc803a5c9052f15e63310aa81a43498d7c55542beb18564db88a9d75a176fa44"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
|
@ -104,6 +192,64 @@
|
|||
],
|
||||
"version": "==4.4.1"
|
||||
},
|
||||
"paramiko": {
|
||||
"hashes": [
|
||||
"sha256:99f0179bdc176281d21961a003ffdb2ec369daac1a1007241f53374e376576cf",
|
||||
"sha256:f4b2edfa0d226b70bd4ca31ea7e389325990283da23465d572ed1f70a7583041"
|
||||
],
|
||||
"version": "==2.6.0"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:125713564d8cfed7610e52444c9769b8dcb0b55e25cc7841f2290ee7bc86636f",
|
||||
"sha256:1accdb7a47e51503be64d9a57543964ba674edac103215576399d2d0e34eac77",
|
||||
"sha256:27003d12d4f68e3cbea9eb67427cab3bfddd47ff90670cb367fcd7a3a89b9657",
|
||||
"sha256:3264f3c431a631b0b31e9db2ae8c927b79fc1a7b1b06b31e8e5bcf2af91fe896",
|
||||
"sha256:3c5ab0f5c71ca5af27143e60613729e3488bb45f6d3f143dc918a20af8bab0bf",
|
||||
"sha256:45dcf8758873e3f69feab075e5f3177270739f146255225474ee0b90429adef6",
|
||||
"sha256:56a77d61a91186cc5676d8e11b36a5feb513873e4ae88d2ee5cf530d52bbcd3b",
|
||||
"sha256:5984e4947bbcef5bd849d6244aec507d31786f2dd3344139adc1489fb403b300",
|
||||
"sha256:6b0441da73796dd00821763bb4119674eaf252776beb50ae3883bed179a60b2a",
|
||||
"sha256:6f6677c5ade94d4fe75a912926d6796d5c71a2a90c2aeefe0d6f211d75c74789",
|
||||
"sha256:84a825a9418d7196e2acc48f8746cf1ee75877ed2f30433ab92a133f3eaf8fbe",
|
||||
"sha256:b842c34fe043ccf78b4a6cf1019d7b80113707d68c88842d061fa2b8fb6ddedc",
|
||||
"sha256:ca33d2f09dae149a1dcf942d2d825ebb06343b77b437198c9e2ef115cf5d5bc1",
|
||||
"sha256:db83b5c12c0cd30150bb568e6feb2435c49ce4e68fe2d7b903113f0e221e58fe",
|
||||
"sha256:f50f3b1c5c1c1334ca7ce9cad5992f098f460ffd6388a3cabad10b66c2006b09",
|
||||
"sha256:f99f127909731cafb841c52f9216e447d3e4afb99b17bebfad327a75aee206de"
|
||||
],
|
||||
"version": "==3.10.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
|
||||
],
|
||||
"version": "==2.19"
|
||||
},
|
||||
"pynacl": {
|
||||
"hashes": [
|
||||
"sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255",
|
||||
"sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c",
|
||||
"sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e",
|
||||
"sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae",
|
||||
"sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621",
|
||||
"sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56",
|
||||
"sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39",
|
||||
"sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310",
|
||||
"sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1",
|
||||
"sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a",
|
||||
"sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786",
|
||||
"sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b",
|
||||
"sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b",
|
||||
"sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f",
|
||||
"sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20",
|
||||
"sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415",
|
||||
"sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715",
|
||||
"sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1",
|
||||
"sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
|
@ -136,10 +282,10 @@
|
|||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||
"sha256:ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2",
|
||||
"sha256:f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396"
|
||||
],
|
||||
"version": "==19.1.0"
|
||||
"version": "==19.2.0"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
|
@ -180,78 +326,78 @@
|
|||
},
|
||||
"grpcio": {
|
||||
"hashes": [
|
||||
"sha256:1303578092f1f6e4bfbc354c04ac422856c393723d3ffa032fff0f7cb5cfd693",
|
||||
"sha256:229c6b313cd82bec8f979b059d87f03cc1a48939b543fe170b5a9c5cf6a6bc69",
|
||||
"sha256:3cd3d99a8b5568d0d186f9520c16121a0f2a4bcad8e2b9884b76fb88a85a7774",
|
||||
"sha256:41cfb222db358227521f9638a6fbc397f310042a4db5539a19dea01547c621cd",
|
||||
"sha256:43330501660f636fd6547d1e196e395cd1e2c2ae57d62219d6184a668ffebda0",
|
||||
"sha256:45d7a2bd8b4f25a013296683f4140d636cdbb507d94a382ea5029a21e76b1648",
|
||||
"sha256:47dc935658a13b25108823dabd010194ddea9610357c5c1ef1ad7b3f5157ebee",
|
||||
"sha256:480aa7e2b56238badce0b9413a96d5b4c90c3bfbd79eba5a0501e92328d9669e",
|
||||
"sha256:4a0934c8b0f97e1d8c18e76c45afc0d02d33ab03125258179f2ac6c7a13f3626",
|
||||
"sha256:5624dab19e950f99e560400c59d87b685809e4cfcb2c724103f1ab14c06071f7",
|
||||
"sha256:60515b1405bb3dadc55e6ca99429072dad3e736afcf5048db5452df5572231ff",
|
||||
"sha256:610f97ebae742a57d336a69b09a9c7d7de1f62aa54aaa8adc635b38f55ba4382",
|
||||
"sha256:64ea189b2b0859d1f7b411a09185028744d494ef09029630200cc892e366f169",
|
||||
"sha256:686090c6c1e09e4f49585b8508d0a31d58bc3895e4049ea55b197d1381e9f70f",
|
||||
"sha256:7745c365195bb0605e3d47b480a2a4d1baa8a41a5fd0a20de5fa48900e2c886a",
|
||||
"sha256:79491e0d2b77a1c438116bf9e5f9e2e04e78b78524615e2ce453eff62db59a09",
|
||||
"sha256:825177dd4c601c487836b7d6b4ba268db59787157911c623ba59a7c03c8d3adc",
|
||||
"sha256:8a060e1f72fb94eee8a035ed29f1201ce903ad14cbe27bda56b4a22a8abda045",
|
||||
"sha256:90168cc6353e2766e47b650c963f21cfff294654b10b3a14c67e26a4e3683634",
|
||||
"sha256:94b7742734bceeff6d8db5edb31ac844cb68fc7f13617eca859ff1b78bb20ba1",
|
||||
"sha256:962aebf2dd01bbb2cdb64580e61760f1afc470781f9ecd5fe8f3d8dcd8cf4556",
|
||||
"sha256:9c8d9eacdce840b72eee7924c752c31b675f8aec74790e08cff184a4ea8aa9c1",
|
||||
"sha256:af5b929debc336f6bab9b0da6915f9ee5e41444012aed6a79a3c7e80d7662fdf",
|
||||
"sha256:b9cdb87fc77e9a3eabdc42a512368538d648fa0760ad30cf97788076985c790a",
|
||||
"sha256:c5e6380b90b389454669dc67d0a39fb4dc166416e01308fcddd694236b8329ef",
|
||||
"sha256:d60c90fe2bfbee735397bf75a2f2c4e70c5deab51cd40c6e4fa98fae018c8db6",
|
||||
"sha256:d8582c8b1b1063249da1588854251d8a91df1e210a328aeb0ece39da2b2b763b",
|
||||
"sha256:ddbf86ba3aa0ad8fed2867910d2913ee237d55920b55f1d619049b3399f04efc",
|
||||
"sha256:e46bc0664c5c8a0545857aa7a096289f8db148e7f9cca2d0b760113e8994bddc",
|
||||
"sha256:f6437f70ec7fed0ca3a0eef1146591bb754b418bb6c6b21db74f0333d624e135",
|
||||
"sha256:f71693c3396530c6b00773b029ea85e59272557e9bd6077195a6593e4229892a",
|
||||
"sha256:f79f7455f8fbd43e8e9d61914ecf7f48ba1c8e271801996fef8d6a8f3cc9f39f"
|
||||
"sha256:0302331e014fc4bac028b6ad480b33f7abfe20b9bdcca7be417124dda8f22115",
|
||||
"sha256:0aa0cce9c5eb1261b32173a20ed42b51308d55ce28ecc2021e868b3cb90d9503",
|
||||
"sha256:0c83947575300499adbc308e986d754e7f629be0bdd9bea1ffdd5cf76e1f1eff",
|
||||
"sha256:0ca26ff968d45efd4ef73447c4d4b34322ea8c7d06fbb6907ce9e5db78f1bbcb",
|
||||
"sha256:0cf80a7955760c2498f8821880242bb657d70998065ff0d2a082de5ffce230a7",
|
||||
"sha256:0d40706e57d9833fe0e023a08b468f33940e8909affa12547874216d36bba208",
|
||||
"sha256:11872069156de34c6f3f9a1deb46cc88bc35dfde88262c4c73eb22b39b16fc55",
|
||||
"sha256:16065227faae0ab0abf1789bfb92a2cd2ab5da87630663f93f8178026da40e0d",
|
||||
"sha256:1e33778277685f6fabb22539136269c87c029e39b6321ef1a639b756a1c0a408",
|
||||
"sha256:2b16be15b1ae656bc7a36642b8c7045be2dde2048bb4b67478003e9d9db8022a",
|
||||
"sha256:3701dfca3ada27ceef0d17f728ce9dfef155ed20c57979c2b05083082258c6c1",
|
||||
"sha256:41912ecaf482abf2de74c69f509878f99223f5dd6b2de1a09c955afd4de3cf9b",
|
||||
"sha256:4332cbd20544fe7406910137590f38b5b3a1f6170258e038652cf478c639430f",
|
||||
"sha256:44068ecbdc6467c2bff4d8198816c8a2701b6dd1ec16078fceb6adc7c1f577d6",
|
||||
"sha256:53115960e37059420e2d16a4b04b00dd2ab3b6c3c67babd01ffbfdcd7881a69b",
|
||||
"sha256:6e7027bcd4070414751e2a5e60706facb98a1fc636497c9bac5442fe37b8ae6b",
|
||||
"sha256:6ff57fb2f07b7226b5bec89e8e921ea9bd220f35f11e094f2ba38f09eecd49c6",
|
||||
"sha256:73240e244d7644654bbda1f309f4911748b6a1804b7a8897ddbe8a04c90f7407",
|
||||
"sha256:785234bbc469bc75e26c868789a2080ffb30bd6e93930167797729889ad06b0b",
|
||||
"sha256:82f9d3c7f91d2d1885631335c003c5d45ae1cd69cc0bc4893f21fef50b8151bc",
|
||||
"sha256:86bdc2a965510658407a1372eb61f0c92f763fdfb2795e4d038944da4320c950",
|
||||
"sha256:95e925b56676a55e6282b3de80a1cbad5774072159779c61eac02791dface049",
|
||||
"sha256:96673bb4f14bd3263613526d1e7e33fdb38a9130e3ce87bf52314965706e1900",
|
||||
"sha256:970014205e76920484679035b6fb4b16e02fc977e5aac4d22025da849c79dab9",
|
||||
"sha256:ace5e8bf11a1571f855f5dab38a9bd34109b6c9bc2864abf24a597598c7e3695",
|
||||
"sha256:ad375f03eb3b9cb75a24d91eab8609e134d34605f199efc41e20dd642bdac855",
|
||||
"sha256:b819c4c7dcf0de76788ce5f95daad6d4e753d6da2b6a5f84e5bb5b5ce95fddc4",
|
||||
"sha256:c17943fd340cbd906db49f3f03c7545e5a66b617e8348b2c7a0d2c759d216af1",
|
||||
"sha256:d21247150dea86dabd3b628d8bc4b563036db3d332b3f4db3c5b1b0b122cb4f6",
|
||||
"sha256:d4d500a7221116de9767229ff5dd10db91f789448d85befb0adf5a37b0cd83b5",
|
||||
"sha256:e2a942a3cfccbbca21a90c144867112698ef36486345c285da9e98c466f22b22",
|
||||
"sha256:e983273dca91cb8a5043bc88322eb48e2b8d4e4998ff441a1ee79ced89db3909"
|
||||
],
|
||||
"version": "==1.23.0"
|
||||
"version": "==1.24.1"
|
||||
},
|
||||
"grpcio-tools": {
|
||||
"hashes": [
|
||||
"sha256:056f2a274edda4315e825ac2e3a9536f5415b43aa51669196860c8de6e76d847",
|
||||
"sha256:0c953251585fdcd422072e4b7f4243fce215f22e21db94ec83c5970e41db6e18",
|
||||
"sha256:142a73f5769f37bf2e4a8e4a77ef60f7af5f55635f60428322b49c87bd8f9cc0",
|
||||
"sha256:1b333e2a068d8ef89a01eb23a098d2a789659c3178de79da9bd3d0ffb944cc6d",
|
||||
"sha256:2124f19cc51d63405a0204ae38ef355732ab0a235975ab41ff6f6f9701905432",
|
||||
"sha256:24c3a04adfb6c6f1bc4a2f8498d7661ca296ae352b498e538832c22ddde7bf81",
|
||||
"sha256:3a2054e9640cbdd0ce8a345afb86be52875c5a8f9f5973a5c64791a8002da2dd",
|
||||
"sha256:3fd15a09eecef83440ac849dcda2ff522f8ee1603ebfcdbb0e9b320ef2012e41",
|
||||
"sha256:457e7a7dfa0b6bb608a766edba6f20c9d626a790df802016b930ad242fec4470",
|
||||
"sha256:49ad5661d54ff0d164e4b441ee5e05191187d497380afa16d36d72eb8ef048de",
|
||||
"sha256:561078e425d21a6720c3c3828385d949e24c0765e2852a46ecc3ad3fca2706e5",
|
||||
"sha256:5a4f65ab06b32dc34112ed114dee3b698c8463670474334ece5b0b531073804c",
|
||||
"sha256:8883e0e34676356d219a4cd37d239c3ead655cc550836236b52068e091416fff",
|
||||
"sha256:8d2b45b1faf81098780e07d6a1c207b328b07e913160b8baa7e2e8d89723e06c",
|
||||
"sha256:b0ebddb6ecc4c161369f93bb3a74c6120a498d3ddc759b64679709a885dd6d4f",
|
||||
"sha256:b786ba4842c50de865dd3885b5570690a743e84a327b7213dd440eb0e6b996f8",
|
||||
"sha256:be8efa010f5a80f1862ead80c3b19b5eb97dc954a0f59a1e2487078576105e03",
|
||||
"sha256:c29106eaff0e2e708a9a89628dc0134ef145d0d3631f0ef421c09f380c30e354",
|
||||
"sha256:c3c71236a056ec961b2b8b3b7c0b3b5a826283bc69c4a1c6415d23b70fea8243",
|
||||
"sha256:cbc35031ec2b29af36947d085a7fbbcd8b79b84d563adf6156103d82565f78db",
|
||||
"sha256:d47307c22744918e803c1eec7263a14f36aaf34fe496bff9ccbcae67c02b40ae",
|
||||
"sha256:db088c98e563c1bb070da5984c8df08b45b61e4d9c6d2a8a1ffeed2af89fd1f3",
|
||||
"sha256:df4dd1cb670062abdacc1fbce41cae4e08a4a212d28dd94fdbbf90615d027f73",
|
||||
"sha256:e3adcf1499ca08d1e036ff44aedf55ed78653d946f4c4426b6e72ab757cc4dec",
|
||||
"sha256:e3b3e32e0cda4dc382ec5bed8599dab644e4b3fc66a9ab54eb58248e207880b9",
|
||||
"sha256:ed524195b35304c670755efa1eca579e5c290a66981f97004a5b2c0d12d6897d",
|
||||
"sha256:edb42432790b1f8ec9f08faf9326d7e5dfe6e1d8c8fe4db39abc0a49c1c76537",
|
||||
"sha256:eff1f995e5aa4cc941b6bbc45b5b57842f8f62bbe1a99427281c2c70cf42312c",
|
||||
"sha256:f2fcdc2669662d77b400f80e20315a3661466e3cb3df1730f8083f9e49465cbc",
|
||||
"sha256:f52ec9926daf48f41389d39d01570967b99c7dbc12bffc134cc3a3c5b5540ba2",
|
||||
"sha256:fd007d67fdfbd2a13bf8a8c8ced8353b42a92ca72dbee54e951d8ddbc6ca12bc",
|
||||
"sha256:ff9045e928dbb7943ea8559bfabebee95a43a830e00bf52c16202d2d805780fb"
|
||||
"sha256:0a849994d7d6411ca6147bb1db042b61ba6232eb5c90c69de5380a441bf80a75",
|
||||
"sha256:0db96ed52816471ceec8807aedf5cb4fd133ca201f614464cb46ca58584edf84",
|
||||
"sha256:1b98720459204e9afa33928e4fd53aeec6598afb7f704ed497f6926c67f12b9b",
|
||||
"sha256:200479310cc083c41a5020f6e5e916a99ee0f7c588b6affe317b96a839120bf4",
|
||||
"sha256:25543b8f2e59ddcc9929d6f6111faa5c474b21580d2996f93347bb55f2ecba84",
|
||||
"sha256:2d4609996616114c155c1e697a9faf604d81f2508cd9a4168a0bafd53c799e24",
|
||||
"sha256:2fdb2a1ed2b3e43514d9c29c9de415c953a46caabbc8a9b7de1439a0c1bd3b89",
|
||||
"sha256:3886a7983d8ae19df0c11a54114d6546fcdf76cf18cdccf25c3b14200fd5478a",
|
||||
"sha256:408d111b9341f107bdafc523e2345471547ffe8a4104e6f2ce690b7a25c4bae5",
|
||||
"sha256:60b3dd5e76c1389fc836bf83675985b92d158ff9a8d3d6d3f0a670f0c227ef13",
|
||||
"sha256:629be7ce8504530b4adbf0425a44dd53007ccb6212344804294888c9662cc38f",
|
||||
"sha256:6af3dde07b1051e954230e650a6ef74073cf993cf473c2078580f8a73c4fe46a",
|
||||
"sha256:7a1e77539d28e90517c55561f40f7872f1348d0e23f25a38d68abbfb5b0eff88",
|
||||
"sha256:87917a18b3b5951b6c9badd7b5ef09f63f61611966b58427b856bdf5c1d68e91",
|
||||
"sha256:8823d0ebd185a77edb506e286c88d06847f75620a033ad96ef9c0fd7efc1d859",
|
||||
"sha256:8bd3e12e1969beb813b861a2a65d4f2d4faaa87de0b60bf7f848da2d8ffc4eb2",
|
||||
"sha256:8f37e9acc46e75ed9786ece89afeacd86182893eacc3f0642d81531b90fbe25f",
|
||||
"sha256:9b358dd2f4142e89d760a52a7a8f4ec5dbaf955e7ada09f703f3a5d05dddd12e",
|
||||
"sha256:9cb43007c4a8aa7adaacf896f5109b578028f23d259615e3fa5866e38855b311",
|
||||
"sha256:9cf594bfbfbf84dcd462b20a4a753362be7ed376d2b5020a083dac24400b7b6c",
|
||||
"sha256:ab79940e5c5ed949e1f95e7f417dd916b0992d29f45d073dd64501a76d128e2c",
|
||||
"sha256:ba8aab6c78a82755477bb8c79f3be0824b297422d1edb21b94ae5a45407bf3ba",
|
||||
"sha256:bcc00b83bf39f6e60a13f0b24ec3951f4d2ae810b01e6e125b7ff238a85da1ac",
|
||||
"sha256:c1fcf5cbe6a2ecdc587b469156520b9128ccdb7c5908060c7d9712cd97e76db5",
|
||||
"sha256:c6e640d39b9615388b59036b29970292b15f4519043e43833e28c674f740d1f7",
|
||||
"sha256:c6ea2c385da620049b17f0135cf9307a4750e9d9c9988e15bfeeaf1f209c4ada",
|
||||
"sha256:cec4f37120f93fe2ab4ab9a7eab9a877163d74c232c93a275a624971f8557b81",
|
||||
"sha256:d2dbb42d237bcdecb7284535ec074c85bbf880124c1cbbff362ed3bd81ed7d41",
|
||||
"sha256:d5c98a41abd4f7de43b256c21bbba2a97c57e25bf6a170927a90638b18f7509c",
|
||||
"sha256:dcf5965a24179aa7dcfa00b5ff70f4f2f202e663657e0c74a642307beecda053",
|
||||
"sha256:e11e3aacf0200d6e00a9b74534e0174738768fe1c41e5aa2f4aab881d6b43afd",
|
||||
"sha256:e550816bdb2e49bba94bcd7f342004a8adbc46e9a25c8c4ed3fd58f2435c655f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.23.0"
|
||||
"version": "==1.24.1"
|
||||
},
|
||||
"identify": {
|
||||
"hashes": [
|
||||
|
@ -262,11 +408,11 @@
|
|||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:652234b6ab8f2506ae58e528b6fbcc668831d3cc758e1bc01ef438d328b68cdb",
|
||||
"sha256:6f264986fb88042bc1f0535fa9a557e6a376cfe5679dc77caac7fe8b5d43d05f"
|
||||
"sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26",
|
||||
"sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==0.22"
|
||||
"version": "==0.23"
|
||||
},
|
||||
"importlib-resources": {
|
||||
"hashes": [
|
||||
|
@ -314,10 +460,10 @@
|
|||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
|
||||
"sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
|
||||
"sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47",
|
||||
"sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"
|
||||
],
|
||||
"version": "==19.1"
|
||||
"version": "==19.2"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
|
@ -336,24 +482,24 @@
|
|||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:00a1b0b352dc7c809749526d1688a64b62ea400c5b05416f93cfb1b11a036295",
|
||||
"sha256:01acbca2d2c8c3f7f235f1842440adbe01bbc379fa1cbdd80753801432b3fae9",
|
||||
"sha256:0a795bca65987b62d6b8a2d934aa317fd1a4d06a6dd4df36312f5b0ade44a8d9",
|
||||
"sha256:0ec035114213b6d6e7713987a759d762dd94e9f82284515b3b7331f34bfaec7f",
|
||||
"sha256:31b18e1434b4907cb0113e7a372cd4d92c047ce7ba0fa7ea66a404d6388ed2c1",
|
||||
"sha256:32a3abf79b0bef073c70656e86d5bd68a28a1fbb138429912c4fc07b9d426b07",
|
||||
"sha256:55f85b7808766e5e3f526818f5e2aeb5ba2edcc45bcccede46a3ccc19b569cb0",
|
||||
"sha256:64ab9bc971989cbdd648c102a96253fdf0202b0c38f15bd34759a8707bdd5f64",
|
||||
"sha256:64cf847e843a465b6c1ba90fb6c7f7844d54dbe9eb731e86a60981d03f5b2e6e",
|
||||
"sha256:917c8662b585470e8fd42f052661fc66d59fccaae450a60044307dcbf82a3335",
|
||||
"sha256:afed9003d7f2be2c3df20f64220c30faec441073731511728a2cb4cab4cd46a6",
|
||||
"sha256:bf8e05d638b585d1752c5a84247134a0350d3a8b73d3632489a014a9f6f1e758",
|
||||
"sha256:d831b047bd69becaf64019a47179eb22118a50dd008340655266a906c69c6417",
|
||||
"sha256:de2760583ed28749ff885789c1cbc6c9c06d6de92fc825740ab99deb2f25ea4d",
|
||||
"sha256:eabc4cf1bc19689af8022ba52fd668564a8d96e0d08f3b4732d26a64255216a4",
|
||||
"sha256:fcff6086c86fb1628d94ea455c7b9de898afc50378042927a59df8065a79a549"
|
||||
"sha256:125713564d8cfed7610e52444c9769b8dcb0b55e25cc7841f2290ee7bc86636f",
|
||||
"sha256:1accdb7a47e51503be64d9a57543964ba674edac103215576399d2d0e34eac77",
|
||||
"sha256:27003d12d4f68e3cbea9eb67427cab3bfddd47ff90670cb367fcd7a3a89b9657",
|
||||
"sha256:3264f3c431a631b0b31e9db2ae8c927b79fc1a7b1b06b31e8e5bcf2af91fe896",
|
||||
"sha256:3c5ab0f5c71ca5af27143e60613729e3488bb45f6d3f143dc918a20af8bab0bf",
|
||||
"sha256:45dcf8758873e3f69feab075e5f3177270739f146255225474ee0b90429adef6",
|
||||
"sha256:56a77d61a91186cc5676d8e11b36a5feb513873e4ae88d2ee5cf530d52bbcd3b",
|
||||
"sha256:5984e4947bbcef5bd849d6244aec507d31786f2dd3344139adc1489fb403b300",
|
||||
"sha256:6b0441da73796dd00821763bb4119674eaf252776beb50ae3883bed179a60b2a",
|
||||
"sha256:6f6677c5ade94d4fe75a912926d6796d5c71a2a90c2aeefe0d6f211d75c74789",
|
||||
"sha256:84a825a9418d7196e2acc48f8746cf1ee75877ed2f30433ab92a133f3eaf8fbe",
|
||||
"sha256:b842c34fe043ccf78b4a6cf1019d7b80113707d68c88842d061fa2b8fb6ddedc",
|
||||
"sha256:ca33d2f09dae149a1dcf942d2d825ebb06343b77b437198c9e2ef115cf5d5bc1",
|
||||
"sha256:db83b5c12c0cd30150bb568e6feb2435c49ce4e68fe2d7b903113f0e221e58fe",
|
||||
"sha256:f50f3b1c5c1c1334ca7ce9cad5992f098f460ffd6388a3cabad10b66c2006b09",
|
||||
"sha256:f99f127909731cafb841c52f9216e447d3e4afb99b17bebfad327a75aee206de"
|
||||
],
|
||||
"version": "==3.9.1"
|
||||
"version": "==3.10.0"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
|
@ -385,11 +531,11 @@
|
|||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:95d13143cc14174ca1a01ec68e84d76ba5d9d493ac02716fd9706c949a505210",
|
||||
"sha256:b78fe2881323bd44fd9bd76e5317173d4316577e7b1cddebae9136a4495ec865"
|
||||
"sha256:13c1c9b22127a77fc684eee24791efafcef343335d855e3573791c68588fe1a5",
|
||||
"sha256:d8ba7be9466f55ef96ba203fc0f90d0cf212f2f927e69186e1353e30bc7f62e5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.1.2"
|
||||
"version": "==5.2.0"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
|
|
|
@ -2,3 +2,6 @@ import logging.config
|
|||
|
||||
# setup default null handler
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
# disable paramiko logging
|
||||
logging.getLogger("paramiko").setLevel(logging.WARNING)
|
||||
|
|
|
@ -22,7 +22,7 @@ from core.emulator.data import (
|
|||
)
|
||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, LinkTypes, MessageFlags, NodeTypes
|
||||
from core.errors import CoreError
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.docker import DockerNode
|
||||
|
@ -882,7 +882,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
logging.debug("sending node command: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
node = self.get_node(session, request.node_id, context)
|
||||
_, output = node.cmd_output(request.command)
|
||||
try:
|
||||
output = node.node_net_cmd(request.command)
|
||||
except CoreCommandError as e:
|
||||
output = e.stderr
|
||||
return core_pb2.NodeCommandResponse(output=output)
|
||||
|
||||
def GetNodeTerminal(self, request, context):
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,7 +15,6 @@ from core.api.tlv import structutils
|
|||
from core.emulator.enumerations import (
|
||||
ConfigTlvs,
|
||||
EventTlvs,
|
||||
EventTypes,
|
||||
ExceptionTlvs,
|
||||
ExecuteTlvs,
|
||||
FileTlvs,
|
||||
|
@ -1017,20 +1016,3 @@ def str_to_list(value):
|
|||
return None
|
||||
|
||||
return value.split("|")
|
||||
|
||||
|
||||
def state_name(value):
|
||||
"""
|
||||
Helper to convert state number into state name using event types.
|
||||
|
||||
:param int value: state value to derive name from
|
||||
:return: state name
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
try:
|
||||
value = EventTypes(value).name
|
||||
except ValueError:
|
||||
value = "unknown"
|
||||
|
||||
return value
|
||||
|
|
|
@ -37,7 +37,7 @@ from core.emulator.enumerations import (
|
|||
RegisterTlvs,
|
||||
SessionTlvs,
|
||||
)
|
||||
from core.errors import CoreError
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.nodes.network import WlanNode
|
||||
from core.services.coreservices import ServiceManager, ServiceShim
|
||||
|
@ -86,6 +86,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
self.master = False
|
||||
self.session = None
|
||||
self.session_clients = {}
|
||||
|
||||
# core emulator
|
||||
self.coreemu = server.coreemu
|
||||
|
@ -138,8 +139,9 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
if self.session:
|
||||
# remove client from session broker and shutdown if there are no clients
|
||||
self.remove_session_handlers()
|
||||
self.session.broker.session_clients.remove(self)
|
||||
if not self.session.broker.session_clients and not self.session.is_active():
|
||||
clients = self.session_clients[self.session.id]
|
||||
clients.remove(self)
|
||||
if not clients and not self.session.is_active():
|
||||
logging.info(
|
||||
"no session clients left and not active, initiating shutdown"
|
||||
)
|
||||
|
@ -407,9 +409,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
tlv_data += coreapi.CoreRegisterTlv.pack(
|
||||
RegisterTlvs.EMULATION_SERVER.value, "core-daemon"
|
||||
)
|
||||
tlv_data += coreapi.CoreRegisterTlv.pack(
|
||||
self.session.broker.config_type, self.session.broker.name
|
||||
)
|
||||
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.UTILITY.value, "broker")
|
||||
tlv_data += coreapi.CoreRegisterTlv.pack(
|
||||
self.session.location.config_type, self.session.location.name
|
||||
)
|
||||
|
@ -533,10 +533,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
:param message: message to handle
|
||||
:return: nothing
|
||||
"""
|
||||
if self.session and self.session.broker.handle_message(message):
|
||||
logging.debug("message not being handled locally")
|
||||
return
|
||||
|
||||
logging.debug(
|
||||
"%s handling message:\n%s", threading.currentThread().getName(), message
|
||||
)
|
||||
|
@ -606,12 +602,11 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
self.session = self.coreemu.create_session(port, master=False)
|
||||
logging.debug("created new session for client: %s", self.session.id)
|
||||
|
||||
# TODO: hack to associate this handler with this sessions broker for broadcasting
|
||||
# TODO: broker needs to be pulled out of session to the server/handler level
|
||||
if self.master:
|
||||
logging.debug("session set to master")
|
||||
self.session.master = True
|
||||
self.session.broker.session_clients.append(self)
|
||||
clients = self.session_clients.setdefault(self.session.id, [])
|
||||
clients.append(self)
|
||||
|
||||
# add handlers for various data
|
||||
self.add_session_handlers()
|
||||
|
@ -643,7 +638,8 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
]:
|
||||
continue
|
||||
|
||||
for client in self.session.broker.session_clients:
|
||||
clients = self.session_clients[self.session.id]
|
||||
for client in clients:
|
||||
if client == self:
|
||||
continue
|
||||
|
||||
|
@ -734,6 +730,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
node_options.icon = message.get_tlv(NodeTlvs.ICON.value)
|
||||
node_options.canvas = message.get_tlv(NodeTlvs.CANVAS.value)
|
||||
node_options.opaque = message.get_tlv(NodeTlvs.OPAQUE.value)
|
||||
node_options.emulation_server = message.get_tlv(NodeTlvs.EMULATION_SERVER.value)
|
||||
|
||||
services = message.get_tlv(NodeTlvs.SERVICES.value)
|
||||
if services:
|
||||
|
@ -887,11 +884,20 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
message.flags & MessageFlags.STRING.value
|
||||
or message.flags & MessageFlags.TEXT.value
|
||||
):
|
||||
# shlex.split() handles quotes within the string
|
||||
if message.flags & MessageFlags.LOCAL.value:
|
||||
status, res = utils.cmd_output(command)
|
||||
try:
|
||||
res = utils.check_cmd(command)
|
||||
status = 0
|
||||
except CoreCommandError as e:
|
||||
res = e.stderr
|
||||
status = e.returncode
|
||||
else:
|
||||
status, res = node.cmd_output(command)
|
||||
try:
|
||||
res = node.node_net_cmd(command)
|
||||
status = 0
|
||||
except CoreCommandError as e:
|
||||
res = e.stderr
|
||||
status = e.returncode
|
||||
logging.info(
|
||||
"done exec cmd=%s with status=%d res=(%d bytes)",
|
||||
command,
|
||||
|
@ -913,7 +919,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
if message.flags & MessageFlags.LOCAL.value:
|
||||
utils.mute_detach(command)
|
||||
else:
|
||||
node.cmd(command, wait=False)
|
||||
node.node_net_cmd(command, wait=False)
|
||||
except CoreError:
|
||||
logging.exception("error getting object: %s", node_num)
|
||||
# XXX wait and queue this message to try again later
|
||||
|
@ -1018,8 +1024,9 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
# find the session containing this client and set the session to master
|
||||
for _id in self.coreemu.sessions:
|
||||
clients = self.session_clients[_id]
|
||||
if self in clients:
|
||||
session = self.coreemu.sessions[_id]
|
||||
if self in session.broker.session_clients:
|
||||
logging.debug("setting session to master: %s", session.id)
|
||||
session.master = True
|
||||
break
|
||||
|
@ -1068,7 +1075,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
self.handle_config_location(message_type, config_data)
|
||||
elif config_data.object == self.session.metadata.name:
|
||||
replies = self.handle_config_metadata(message_type, config_data)
|
||||
elif config_data.object == self.session.broker.name:
|
||||
elif config_data.object == "broker":
|
||||
self.handle_config_broker(message_type, config_data)
|
||||
elif config_data.object == self.session.services.name:
|
||||
replies = self.handle_config_services(message_type, config_data)
|
||||
|
@ -1173,7 +1180,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
def handle_config_broker(self, message_type, config_data):
|
||||
if message_type not in [ConfigFlags.REQUEST, ConfigFlags.RESET]:
|
||||
session_id = config_data.session
|
||||
if not config_data.data_values:
|
||||
logging.info("emulation server data missing")
|
||||
else:
|
||||
|
@ -1185,29 +1191,10 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
for server in server_list:
|
||||
server_items = server.split(":")
|
||||
name, host, port = server_items[:3]
|
||||
|
||||
if host == "":
|
||||
host = None
|
||||
|
||||
if port == "":
|
||||
port = None
|
||||
else:
|
||||
port = int(port)
|
||||
|
||||
if session_id is not None:
|
||||
# receive session ID and my IP from master
|
||||
self.session.broker.session_id_master = int(
|
||||
session_id.split("|")[0]
|
||||
)
|
||||
self.session.broker.myip = host
|
||||
host = None
|
||||
port = None
|
||||
|
||||
# this connects to the server immediately; maybe we should wait
|
||||
# or spin off a new "client" thread here
|
||||
self.session.broker.addserver(name, host, port)
|
||||
self.session.broker.setupserver(name)
|
||||
name, host, _ = server_items[:3]
|
||||
self.session.distributed.add_server(name, host)
|
||||
elif message_type == ConfigFlags.RESET:
|
||||
self.session.distributed.shutdown()
|
||||
|
||||
def handle_config_services(self, message_type, config_data):
|
||||
replies = []
|
||||
|
@ -1833,11 +1820,9 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
# remove client from session broker and shutdown if needed
|
||||
self.remove_session_handlers()
|
||||
self.session.broker.session_clients.remove(self)
|
||||
if (
|
||||
not self.session.broker.session_clients
|
||||
and not self.session.is_active()
|
||||
):
|
||||
clients = self.session_clients[self.session.id]
|
||||
clients.remove(self)
|
||||
if not clients and not self.session.is_active():
|
||||
self.coreemu.delete_session(self.session.id)
|
||||
|
||||
# set session to join
|
||||
|
@ -1846,7 +1831,8 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
# add client to session broker and set master if needed
|
||||
if self.master:
|
||||
self.session.master = True
|
||||
self.session.broker.session_clients.append(self)
|
||||
clients = self.session_clients.setdefault(self.session.id, [])
|
||||
clients.append(self)
|
||||
|
||||
# add broadcast handlers
|
||||
logging.info("adding session broadcast handlers")
|
||||
|
@ -2097,6 +2083,7 @@ class CoreUdpHandler(CoreHandler):
|
|||
logging.debug("session handling message: %s", session.session_id)
|
||||
self.session = session
|
||||
self.handle_message(message)
|
||||
self.session.sdt.handle_distributed(message)
|
||||
self.broadcast(message)
|
||||
else:
|
||||
logging.error(
|
||||
|
@ -2121,6 +2108,7 @@ class CoreUdpHandler(CoreHandler):
|
|||
if session or message.message_type == MessageTypes.REGISTER.value:
|
||||
self.session = session
|
||||
self.handle_message(message)
|
||||
self.session.sdt.handle_distributed(message)
|
||||
self.broadcast(message)
|
||||
else:
|
||||
logging.error(
|
||||
|
@ -2131,7 +2119,8 @@ class CoreUdpHandler(CoreHandler):
|
|||
if not isinstance(message, (coreapi.CoreNodeMessage, coreapi.CoreLinkMessage)):
|
||||
return
|
||||
|
||||
for client in self.session.broker.session_clients:
|
||||
clients = self.session_clients[self.session.id]
|
||||
for client in clients:
|
||||
try:
|
||||
client.sendall(message.raw_message)
|
||||
except IOError:
|
||||
|
|
|
@ -2,14 +2,12 @@
|
|||
emane.py: definition of an Emane class for implementing configuration control of an EMANE emulation.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
|
||||
from core import utils
|
||||
from core.api.tlv import coreapi, dataconversion
|
||||
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
|
||||
from core.config import ConfigGroup, Configuration, ModelManager
|
||||
from core.emane import emanemanifest
|
||||
from core.emane.bypass import EmaneBypassModel
|
||||
from core.emane.commeffect import EmaneCommEffectModel
|
||||
|
@ -18,14 +16,7 @@ from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
|||
from core.emane.nodes import EmaneNet
|
||||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emane.tdma import EmaneTdmaModel
|
||||
from core.emulator.enumerations import (
|
||||
ConfigDataTypes,
|
||||
ConfigFlags,
|
||||
ConfigTlvs,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.emulator.enumerations import ConfigDataTypes, RegisterTlvs
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.xml import emanexml
|
||||
|
||||
|
@ -75,8 +66,6 @@ class EmaneManager(ModelManager):
|
|||
self.session = session
|
||||
self._emane_nets = {}
|
||||
self._emane_node_lock = threading.Lock()
|
||||
self._ifccounts = {}
|
||||
self._ifccountslock = threading.Lock()
|
||||
# port numbers are allocated from these counters
|
||||
self.platformport = self.session.options.get_config_int(
|
||||
"emane_platform_port", 8100
|
||||
|
@ -91,7 +80,6 @@ class EmaneManager(ModelManager):
|
|||
self.emane_config = EmaneGlobalModel(session)
|
||||
self.set_configs(self.emane_config.default_values())
|
||||
|
||||
session.broker.handlers.add(self.handledistributed)
|
||||
self.service = None
|
||||
self.event_device = None
|
||||
self.emane_check()
|
||||
|
@ -151,8 +139,10 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
try:
|
||||
# check for emane
|
||||
emane_version = utils.check_cmd(["emane", "--version"])
|
||||
args = "emane --version"
|
||||
emane_version = utils.check_cmd(args)
|
||||
logging.info("using EMANE: %s", emane_version)
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(args))
|
||||
|
||||
# load default emane models
|
||||
self.load_models(EMANE_MODELS)
|
||||
|
@ -278,7 +268,6 @@ class EmaneManager(ModelManager):
|
|||
return EmaneManager.NOT_NEEDED
|
||||
|
||||
# control network bridge required for EMANE 0.9.2
|
||||
# - needs to be configured before checkdistributed() for distributed
|
||||
# - needs to exist when eventservice binds to it (initeventservice)
|
||||
if self.session.master:
|
||||
otadev = self.get_config("otamanagerdevice")
|
||||
|
@ -293,10 +282,9 @@ class EmaneManager(ModelManager):
|
|||
)
|
||||
return EmaneManager.NOT_READY
|
||||
|
||||
ctrlnet = self.session.add_remove_control_net(
|
||||
self.session.add_remove_control_net(
|
||||
net_index=netidx, remove=False, conf_required=False
|
||||
)
|
||||
self.distributedctrlnet(ctrlnet)
|
||||
eventdev = self.get_config("eventservicedevice")
|
||||
logging.debug("emane event service device: eventdev(%s)", eventdev)
|
||||
if eventdev != otadev:
|
||||
|
@ -309,18 +297,9 @@ class EmaneManager(ModelManager):
|
|||
)
|
||||
return EmaneManager.NOT_READY
|
||||
|
||||
ctrlnet = self.session.add_remove_control_net(
|
||||
self.session.add_remove_control_net(
|
||||
net_index=netidx, remove=False, conf_required=False
|
||||
)
|
||||
self.distributedctrlnet(ctrlnet)
|
||||
|
||||
if self.checkdistributed():
|
||||
# we are slave, but haven't received a platformid yet
|
||||
platform_id_start = "platform_id_start"
|
||||
default_values = self.emane_config.default_values()
|
||||
value = self.get_config(platform_id_start)
|
||||
if value == default_values[platform_id_start]:
|
||||
return EmaneManager.NOT_READY
|
||||
|
||||
self.check_node_models()
|
||||
return EmaneManager.SUCCESS
|
||||
|
@ -409,9 +388,6 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
stop all EMANE daemons
|
||||
"""
|
||||
with self._ifccountslock:
|
||||
self._ifccounts.clear()
|
||||
|
||||
with self._emane_node_lock:
|
||||
if not self._emane_nets:
|
||||
return
|
||||
|
@ -420,92 +396,6 @@ class EmaneManager(ModelManager):
|
|||
self.stopdaemons()
|
||||
self.stopeventmonitor()
|
||||
|
||||
def handledistributed(self, message):
|
||||
"""
|
||||
Broker handler for processing CORE API messages as they are
|
||||
received. This is used to snoop the Link add messages to get NEM
|
||||
counts of NEMs that exist on other servers.
|
||||
"""
|
||||
if (
|
||||
message.message_type == MessageTypes.LINK.value
|
||||
and message.flags & MessageFlags.ADD.value
|
||||
):
|
||||
nn = message.node_numbers()
|
||||
# first node is always link layer node in Link add message
|
||||
if nn[0] in self.session.broker.network_nodes:
|
||||
serverlist = self.session.broker.getserversbynode(nn[1])
|
||||
for server in serverlist:
|
||||
with self._ifccountslock:
|
||||
if server not in self._ifccounts:
|
||||
self._ifccounts[server] = 1
|
||||
else:
|
||||
self._ifccounts[server] += 1
|
||||
|
||||
def checkdistributed(self):
|
||||
"""
|
||||
Check for EMANE nodes that exist on multiple emulation servers and
|
||||
coordinate the NEM id and port number space.
|
||||
If we are the master EMANE node, return False so initialization will
|
||||
proceed as normal; otherwise slaves return True here and
|
||||
initialization is deferred.
|
||||
"""
|
||||
# check with the session if we are the "master" Emane object?
|
||||
master = False
|
||||
|
||||
with self._emane_node_lock:
|
||||
if self._emane_nets:
|
||||
master = self.session.master
|
||||
logging.info("emane check distributed as master: %s.", master)
|
||||
|
||||
# we are not the master Emane object, wait for nem id and ports
|
||||
if not master:
|
||||
return True
|
||||
|
||||
nemcount = 0
|
||||
with self._emane_node_lock:
|
||||
for key in self._emane_nets:
|
||||
emane_node = self._emane_nets[key]
|
||||
nemcount += emane_node.numnetif()
|
||||
|
||||
nemid = int(self.get_config("nem_id_start"))
|
||||
nemid += nemcount
|
||||
|
||||
platformid = int(self.get_config("platform_id_start"))
|
||||
|
||||
# build an ordered list of servers so platform ID is deterministic
|
||||
servers = []
|
||||
for key in sorted(self._emane_nets):
|
||||
for server in self.session.broker.getserversbynode(key):
|
||||
if server not in servers:
|
||||
servers.append(server)
|
||||
|
||||
servers.sort(key=lambda x: x.name)
|
||||
for server in servers:
|
||||
if server.name == "localhost":
|
||||
continue
|
||||
|
||||
if server.sock is None:
|
||||
continue
|
||||
|
||||
platformid += 1
|
||||
|
||||
# create temporary config for updating distributed nodes
|
||||
typeflags = ConfigFlags.UPDATE.value
|
||||
config = copy.deepcopy(self.get_configs())
|
||||
config["platform_id_start"] = str(platformid)
|
||||
config["nem_id_start"] = str(nemid)
|
||||
config_data = ConfigShim.config_data(
|
||||
0, None, typeflags, self.emane_config, config
|
||||
)
|
||||
message = dataconversion.convert_config(config_data)
|
||||
server.sock.send(message)
|
||||
# increment nemid for next server by number of interfaces
|
||||
with self._ifccountslock:
|
||||
if server in self._ifccounts:
|
||||
nemid += self._ifccounts[server]
|
||||
|
||||
return False
|
||||
|
||||
def buildxml(self):
|
||||
"""
|
||||
Build XML files required to run EMANE on each node.
|
||||
|
@ -522,52 +412,6 @@ class EmaneManager(ModelManager):
|
|||
self.buildnemxml()
|
||||
self.buildeventservicexml()
|
||||
|
||||
# TODO: remove need for tlv messaging
|
||||
def distributedctrlnet(self, ctrlnet):
|
||||
"""
|
||||
Distributed EMANE requires multiple control network prefixes to
|
||||
be configured. This generates configuration for slave control nets
|
||||
using the default list of prefixes.
|
||||
"""
|
||||
# slave server
|
||||
session = self.session
|
||||
if not session.master:
|
||||
return
|
||||
|
||||
# not distributed
|
||||
servers = session.broker.getservernames()
|
||||
if len(servers) < 2:
|
||||
return
|
||||
|
||||
# normal Config messaging will distribute controlnets
|
||||
prefix = session.options.get_config("controlnet", default="")
|
||||
prefixes = prefix.split()
|
||||
if len(prefixes) < len(servers):
|
||||
logging.info(
|
||||
"setting up default controlnet prefixes for distributed (%d configured)",
|
||||
len(prefixes),
|
||||
)
|
||||
prefix = ctrlnet.DEFAULT_PREFIX_LIST[0]
|
||||
prefixes = prefix.split()
|
||||
servers.remove("localhost")
|
||||
servers.insert(0, "localhost")
|
||||
prefix = " ".join("%s:%s" % (s, prefixes[i]) for i, s in enumerate(servers))
|
||||
|
||||
# this generates a config message having controlnet prefix assignments
|
||||
logging.info("setting up controlnet prefixes for distributed: %s", prefix)
|
||||
vals = "controlnet=%s" % prefix
|
||||
tlvdata = b""
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, "session")
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, 0)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, vals)
|
||||
rawmsg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
||||
msghdr = rawmsg[: coreapi.CoreMessage.header_len]
|
||||
msg = coreapi.CoreConfMessage(
|
||||
flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len :]
|
||||
)
|
||||
logging.debug("sending controlnet message:\n%s", msg)
|
||||
self.session.broker.handle_message(msg)
|
||||
|
||||
def check_node_models(self):
|
||||
"""
|
||||
Associate EMANE model classes with EMANE network nodes.
|
||||
|
@ -646,14 +490,6 @@ class EmaneManager(ModelManager):
|
|||
emane_net = self._emane_nets[key]
|
||||
emanexml.build_xml_files(self, emane_net)
|
||||
|
||||
def buildtransportxml(self):
|
||||
"""
|
||||
Calls emanegentransportxml using a platform.xml file to build the transportdaemon*.xml.
|
||||
"""
|
||||
utils.check_cmd(
|
||||
["emanegentransportxml", "platform.xml"], cwd=self.session.session_dir
|
||||
)
|
||||
|
||||
def buildeventservicexml(self):
|
||||
"""
|
||||
Build the libemaneeventservice.xml file if event service options
|
||||
|
@ -679,8 +515,12 @@ class EmaneManager(ModelManager):
|
|||
return
|
||||
|
||||
dev = self.get_config("eventservicedevice")
|
||||
|
||||
emanexml.create_event_service_xml(group, port, dev, self.session.session_dir)
|
||||
self.session.distributed.execute(
|
||||
lambda x: emanexml.create_event_service_xml(
|
||||
group, port, dev, self.session.session_dir, x
|
||||
)
|
||||
)
|
||||
|
||||
def startdaemons(self):
|
||||
"""
|
||||
|
@ -695,9 +535,9 @@ class EmaneManager(ModelManager):
|
|||
logging.info("setting user-defined EMANE log level: %d", cfgloglevel)
|
||||
loglevel = str(cfgloglevel)
|
||||
|
||||
emanecmd = ["emane", "-d", "-l", loglevel]
|
||||
emanecmd = "emane -d -l %s" % loglevel
|
||||
if realtime:
|
||||
emanecmd += ("-r",)
|
||||
emanecmd += " -r"
|
||||
|
||||
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
|
||||
otadev = self.get_config("otamanagerdevice")
|
||||
|
@ -740,12 +580,12 @@ class EmaneManager(ModelManager):
|
|||
node.node_net_client.create_route(eventgroup, eventdev)
|
||||
|
||||
# start emane
|
||||
args = emanecmd + [
|
||||
"-f",
|
||||
args = "%s -f %s %s" % (
|
||||
emanecmd,
|
||||
os.path.join(path, "emane%d.log" % n),
|
||||
os.path.join(path, "platform%d.xml" % n),
|
||||
]
|
||||
output = node.check_cmd(args)
|
||||
)
|
||||
output = node.node_net_cmd(args)
|
||||
logging.info("node(%s) emane daemon running: %s", node.name, args)
|
||||
logging.info("node(%s) emane daemon output: %s", node.name, output)
|
||||
|
||||
|
@ -753,17 +593,20 @@ class EmaneManager(ModelManager):
|
|||
return
|
||||
|
||||
path = self.session.session_dir
|
||||
emanecmd += ["-f", os.path.join(path, "emane.log")]
|
||||
args = emanecmd + [os.path.join(path, "platform.xml")]
|
||||
utils.check_cmd(args, cwd=path)
|
||||
logging.info("host emane daemon running: %s", args)
|
||||
emanecmd += " -f %s" % os.path.join(path, "emane.log")
|
||||
emanecmd += " %s" % os.path.join(path, "platform.xml")
|
||||
utils.check_cmd(emanecmd, cwd=path)
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(emanecmd, cwd=path))
|
||||
logging.info("host emane daemon running: %s", emanecmd)
|
||||
|
||||
def stopdaemons(self):
|
||||
"""
|
||||
Kill the appropriate EMANE daemons.
|
||||
"""
|
||||
# TODO: we may want to improve this if we had the PIDs from the specific EMANE daemons that we"ve started
|
||||
args = ["killall", "-q", "emane"]
|
||||
# TODO: we may want to improve this if we had the PIDs from the specific EMANE
|
||||
# daemons that we"ve started
|
||||
kill_emaned = "killall -q emane"
|
||||
kill_transortd = "killall -q emanetransportd"
|
||||
stop_emane_on_host = False
|
||||
for node in self.getnodes():
|
||||
if hasattr(node, "transport_type") and node.transport_type == "raw":
|
||||
|
@ -771,13 +614,15 @@ class EmaneManager(ModelManager):
|
|||
continue
|
||||
|
||||
if node.up:
|
||||
node.cmd(args, wait=False)
|
||||
node.node_net_cmd(kill_emaned, wait=False)
|
||||
# TODO: RJ45 node
|
||||
|
||||
if stop_emane_on_host:
|
||||
try:
|
||||
utils.check_cmd(args)
|
||||
utils.check_cmd(["killall", "-q", "emanetransportd"])
|
||||
utils.check_cmd(kill_emaned)
|
||||
utils.check_cmd(kill_transortd)
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(kill_emaned))
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(kill_transortd))
|
||||
except CoreCommandError:
|
||||
logging.exception("error shutting down emane daemons")
|
||||
|
||||
|
@ -964,11 +809,17 @@ class EmaneManager(ModelManager):
|
|||
|
||||
def emanerunning(self, node):
|
||||
"""
|
||||
Return True if an EMANE process associated with the given node is running, False otherwise.
|
||||
Return True if an EMANE process associated with the given node is running,
|
||||
False otherwise.
|
||||
"""
|
||||
args = ["pkill", "-0", "-x", "emane"]
|
||||
status = node.cmd(args)
|
||||
return status == 0
|
||||
args = "pkill -0 -x emane"
|
||||
try:
|
||||
node.node_net_cmd(args)
|
||||
result = True
|
||||
except CoreCommandError:
|
||||
result = False
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class EmaneGlobalModel(EmaneModel):
|
||||
|
|
|
@ -102,6 +102,11 @@ class EmaneModel(WirelessModel):
|
|||
mac_name = emanexml.mac_file_name(self, interface)
|
||||
phy_name = emanexml.phy_file_name(self, interface)
|
||||
|
||||
# remote server for file
|
||||
server = None
|
||||
if interface is not None:
|
||||
server = interface.node.server
|
||||
|
||||
# check if this is external
|
||||
transport_type = "virtual"
|
||||
if interface and interface.transport_type == "raw":
|
||||
|
@ -111,16 +116,16 @@ class EmaneModel(WirelessModel):
|
|||
# create nem xml file
|
||||
nem_file = os.path.join(self.session.session_dir, nem_name)
|
||||
emanexml.create_nem_xml(
|
||||
self, config, nem_file, transport_name, mac_name, phy_name
|
||||
self, config, nem_file, transport_name, mac_name, phy_name, server
|
||||
)
|
||||
|
||||
# create mac xml file
|
||||
mac_file = os.path.join(self.session.session_dir, mac_name)
|
||||
emanexml.create_mac_xml(self, config, mac_file)
|
||||
emanexml.create_mac_xml(self, config, mac_file, server)
|
||||
|
||||
# create phy xml file
|
||||
phy_file = os.path.join(self.session.session_dir, phy_name)
|
||||
emanexml.create_phy_xml(self, config, phy_file)
|
||||
emanexml.create_phy_xml(self, config, phy_file, server)
|
||||
|
||||
def post_startup(self):
|
||||
"""
|
||||
|
|
|
@ -29,8 +29,8 @@ class EmaneNet(CoreNetworkBase):
|
|||
type = "wlan"
|
||||
is_emane = True
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True):
|
||||
super(EmaneNet, self).__init__(session, _id, name, start)
|
||||
def __init__(self, session, _id=None, name=None, start=True, server=None):
|
||||
super(EmaneNet, self).__init__(session, _id, name, start, server)
|
||||
self.conf = ""
|
||||
self.up = False
|
||||
self.nemidmap = {}
|
||||
|
|
|
@ -62,4 +62,5 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
|||
logging.info(
|
||||
"setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device
|
||||
)
|
||||
utils.check_cmd(["emaneevent-tdmaschedule", "-i", event_device, schedule])
|
||||
args = "emaneevent-tdmaschedule -i %s %s" % (event_device, schedule)
|
||||
utils.check_cmd(args)
|
||||
|
|
251
daemon/core/emulator/distributed.py
Normal file
251
daemon/core/emulator/distributed.py
Normal file
|
@ -0,0 +1,251 @@
|
|||
"""
|
||||
Defines distributed server functionality.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
from collections import OrderedDict
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from fabric import Connection
|
||||
from invoke import UnexpectedExit
|
||||
|
||||
from core import utils
|
||||
from core.errors import CoreCommandError
|
||||
from core.nodes.interface import GreTap
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
from core.nodes.network import CoreNetwork, CtrlNet
|
||||
|
||||
LOCK = threading.Lock()
|
||||
|
||||
|
||||
class DistributedServer(object):
|
||||
"""
|
||||
Provides distributed server interactions.
|
||||
"""
|
||||
|
||||
def __init__(self, name, host):
|
||||
"""
|
||||
Create a DistributedServer instance.
|
||||
|
||||
:param str name: convenience name to associate with host
|
||||
:param str host: host to connect to
|
||||
"""
|
||||
self.name = name
|
||||
self.host = host
|
||||
self.conn = Connection(host, user="root")
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def remote_cmd(self, cmd, env=None, cwd=None, wait=True):
|
||||
"""
|
||||
Run command remotely using server connection.
|
||||
|
||||
:param str cmd: command to run
|
||||
:param dict env: environment for remote command, default is None
|
||||
:param str cwd: directory to run command in, defaults to None, which is the
|
||||
user's home directory
|
||||
:param bool wait: True to wait for status, False to background process
|
||||
:return: stdout when success
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
|
||||
replace_env = env is not None
|
||||
if not wait:
|
||||
cmd += " &"
|
||||
logging.info(
|
||||
"remote cmd server(%s) cwd(%s) wait(%s): %s", self.host, cwd, wait, cmd
|
||||
)
|
||||
try:
|
||||
if cwd is None:
|
||||
result = self.conn.run(
|
||||
cmd, hide=False, env=env, replace_env=replace_env
|
||||
)
|
||||
else:
|
||||
with self.conn.cd(cwd):
|
||||
result = self.conn.run(
|
||||
cmd, hide=False, env=env, replace_env=replace_env
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except UnexpectedExit as e:
|
||||
stdout, stderr = e.streams_for_display()
|
||||
raise CoreCommandError(e.result.exited, cmd, stdout, stderr)
|
||||
|
||||
def remote_put(self, source, destination):
|
||||
"""
|
||||
Push file to remote server.
|
||||
|
||||
:param str source: source file to push
|
||||
:param str destination: destination file location
|
||||
:return: nothing
|
||||
"""
|
||||
with self.lock:
|
||||
self.conn.put(source, destination)
|
||||
|
||||
def remote_put_temp(self, destination, data):
|
||||
"""
|
||||
Remote push file contents to a remote server, using a temp file as an
|
||||
intermediate step.
|
||||
|
||||
:param str destination: file destination for data
|
||||
:param str data: data to store in remote file
|
||||
:return: nothing
|
||||
"""
|
||||
with self.lock:
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
temp.write(data.encode("utf-8"))
|
||||
temp.close()
|
||||
self.conn.put(temp.name, destination)
|
||||
os.unlink(temp.name)
|
||||
|
||||
|
||||
class DistributedController(object):
|
||||
"""
|
||||
Provides logic for dealing with remote tunnels and distributed servers.
|
||||
"""
|
||||
|
||||
def __init__(self, session):
|
||||
"""
|
||||
Create
|
||||
|
||||
:param session:
|
||||
"""
|
||||
self.session = session
|
||||
self.servers = OrderedDict()
|
||||
self.tunnels = {}
|
||||
self.address = self.session.options.get_config(
|
||||
"distributed_address", default=None
|
||||
)
|
||||
|
||||
def add_server(self, name, host):
|
||||
"""
|
||||
Add distributed server configuration.
|
||||
|
||||
:param str name: distributed server name
|
||||
:param str host: distributed server host address
|
||||
:return: nothing
|
||||
"""
|
||||
server = DistributedServer(name, host)
|
||||
self.servers[name] = server
|
||||
cmd = "mkdir -p %s" % self.session.session_dir
|
||||
server.remote_cmd(cmd)
|
||||
|
||||
def execute(self, func):
|
||||
"""
|
||||
Convenience for executing logic against all distributed servers.
|
||||
|
||||
:param func: function to run, that takes a DistributedServer as a parameter
|
||||
:return: nothing
|
||||
"""
|
||||
for name in self.servers:
|
||||
server = self.servers[name]
|
||||
func(server)
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
Shutdown logic for dealing with distributed tunnels and server session
|
||||
directories.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
# shutdown all tunnels
|
||||
for key in self.tunnels:
|
||||
tunnels = self.tunnels[key]
|
||||
for tunnel in tunnels:
|
||||
tunnel.shutdown()
|
||||
|
||||
# remove all remote session directories
|
||||
for name in self.servers:
|
||||
server = self.servers[name]
|
||||
cmd = "rm -rf %s" % self.session.session_dir
|
||||
server.remote_cmd(cmd)
|
||||
|
||||
# clear tunnels
|
||||
self.tunnels.clear()
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Start distributed network tunnels.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
for node_id in self.session.nodes:
|
||||
node = self.session.nodes[node_id]
|
||||
|
||||
if not isinstance(node, CoreNetwork):
|
||||
continue
|
||||
|
||||
if isinstance(node, CtrlNet) and node.serverintf is not None:
|
||||
continue
|
||||
|
||||
for name in self.servers:
|
||||
server = self.servers[name]
|
||||
self.create_gre_tunnel(node, server)
|
||||
|
||||
def create_gre_tunnel(self, node, server):
|
||||
"""
|
||||
Create gre tunnel using a pair of gre taps between the local and remote server.
|
||||
|
||||
|
||||
:param core.nodes.network.CoreNetwork node: node to create gre tunnel for
|
||||
:param core.emulator.distributed.DistributedServer server: server to create
|
||||
tunnel for
|
||||
:return: local and remote gre taps created for tunnel
|
||||
:rtype: tuple
|
||||
"""
|
||||
host = server.host
|
||||
key = self.tunnel_key(node.id, IpAddress.to_int(host))
|
||||
tunnel = self.tunnels.get(key)
|
||||
if tunnel is not None:
|
||||
return tunnel
|
||||
|
||||
# local to server
|
||||
logging.info(
|
||||
"local tunnel node(%s) to remote(%s) key(%s)", node.name, host, key
|
||||
)
|
||||
local_tap = GreTap(session=self.session, remoteip=host, key=key)
|
||||
local_tap.net_client.create_interface(node.brname, local_tap.localname)
|
||||
|
||||
# server to local
|
||||
logging.info(
|
||||
"remote tunnel node(%s) to local(%s) key(%s)", node.name, self.address, key
|
||||
)
|
||||
remote_tap = GreTap(
|
||||
session=self.session, remoteip=self.address, key=key, server=server
|
||||
)
|
||||
remote_tap.net_client.create_interface(node.brname, remote_tap.localname)
|
||||
|
||||
# save tunnels for shutdown
|
||||
tunnel = (local_tap, remote_tap)
|
||||
self.tunnels[key] = tunnel
|
||||
return tunnel
|
||||
|
||||
def tunnel_key(self, n1_id, n2_id):
|
||||
"""
|
||||
Compute a 32-bit key used to uniquely identify a GRE tunnel.
|
||||
The hash(n1num), hash(n2num) values are used, so node numbers may be
|
||||
None or string values (used for e.g. "ctrlnet").
|
||||
|
||||
:param int n1_id: node one id
|
||||
:param int n2_id: node two id
|
||||
:return: tunnel key for the node pair
|
||||
:rtype: int
|
||||
"""
|
||||
logging.debug("creating tunnel key for: %s, %s", n1_id, n2_id)
|
||||
key = (
|
||||
(self.session.id << 16) ^ utils.hashkey(n1_id) ^ (utils.hashkey(n2_id) << 8)
|
||||
)
|
||||
return key & 0xFFFFFFFF
|
||||
|
||||
def get_tunnel(self, n1_id, n2_id):
|
||||
"""
|
||||
Return the GreTap between two nodes if it exists.
|
||||
|
||||
:param int n1_id: node one id
|
||||
:param int n2_id: node two id
|
||||
:return: gre tap between nodes or None
|
||||
"""
|
||||
key = self.tunnel_key(n1_id, n2_id)
|
||||
logging.debug("checking for tunnel key(%s) in: %s", key, self.tunnels)
|
||||
return self.tunnels.get(key)
|
|
@ -15,11 +15,10 @@ import time
|
|||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from core import constants, utils
|
||||
from core.api.tlv import coreapi
|
||||
from core.api.tlv.broker import CoreBroker
|
||||
from core.emane.emanemanager import EmaneManager
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.data import EventData, ExceptionData, NodeData
|
||||
from core.emulator.distributed import DistributedController
|
||||
from core.emulator.emudata import (
|
||||
IdGen,
|
||||
LinkOptions,
|
||||
|
@ -136,8 +135,10 @@ class Session(object):
|
|||
self.options.set_config(key, value)
|
||||
self.metadata = SessionMetaData()
|
||||
|
||||
# distributed support and logic
|
||||
self.distributed = DistributedController(self)
|
||||
|
||||
# initialize session feature helpers
|
||||
self.broker = CoreBroker(session=self)
|
||||
self.location = CoreLocation()
|
||||
self.mobility = MobilityManager(session=self)
|
||||
self.services = CoreServices(session=self)
|
||||
|
@ -148,7 +149,7 @@ class Session(object):
|
|||
self.services.default_services = {
|
||||
"mdr": ("zebra", "OSPFv3MDR", "IPForward"),
|
||||
"PC": ("DefaultRoute",),
|
||||
"prouter": ("zebra", "OSPFv2", "OSPFv3", "IPForward"),
|
||||
"prouter": (),
|
||||
"router": ("zebra", "OSPFv2", "OSPFv3", "IPForward"),
|
||||
"host": ("DefaultRoute", "SSH"),
|
||||
}
|
||||
|
@ -202,7 +203,7 @@ class Session(object):
|
|||
node_two = self.get_node(node_two_id)
|
||||
|
||||
# both node ids are provided
|
||||
tunnel = self.broker.gettunnel(node_one_id, node_two_id)
|
||||
tunnel = self.distributed.get_tunnel(node_one_id, node_two_id)
|
||||
logging.debug("tunnel between nodes: %s", tunnel)
|
||||
if isinstance(tunnel, GreTapBridge):
|
||||
net_one = tunnel
|
||||
|
@ -666,6 +667,13 @@ class Session(object):
|
|||
if not name:
|
||||
name = "%s%s" % (node_class.__name__, _id)
|
||||
|
||||
# verify distributed server
|
||||
server = self.distributed.servers.get(node_options.emulation_server)
|
||||
if node_options.emulation_server is not None and server is None:
|
||||
raise CoreError(
|
||||
"invalid distributed server: %s" % node_options.emulation_server
|
||||
)
|
||||
|
||||
# create node
|
||||
logging.info(
|
||||
"creating node(%s) id(%s) name(%s) start(%s)",
|
||||
|
@ -681,9 +689,12 @@ class Session(object):
|
|||
name=name,
|
||||
start=start,
|
||||
image=node_options.image,
|
||||
server=server,
|
||||
)
|
||||
else:
|
||||
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
|
||||
node = self.create_node(
|
||||
cls=node_class, _id=_id, name=name, start=start, server=server
|
||||
)
|
||||
|
||||
# set node attributes
|
||||
node.icon = node_options.icon
|
||||
|
@ -866,13 +877,13 @@ class Session(object):
|
|||
|
||||
def clear(self):
|
||||
"""
|
||||
Clear all CORE session data. (objects, hooks, broker)
|
||||
Clear all CORE session data. (nodes, hooks, etc)
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.delete_nodes()
|
||||
self.distributed.shutdown()
|
||||
self.del_hooks()
|
||||
self.broker.reset()
|
||||
self.emane.reset()
|
||||
|
||||
def start_events(self):
|
||||
|
@ -946,11 +957,11 @@ class Session(object):
|
|||
|
||||
# shutdown/cleanup feature helpers
|
||||
self.emane.shutdown()
|
||||
self.broker.shutdown()
|
||||
self.sdt.shutdown()
|
||||
|
||||
# delete all current nodes
|
||||
# remove and shutdown all nodes and tunnels
|
||||
self.delete_nodes()
|
||||
self.distributed.shutdown()
|
||||
|
||||
# remove this sessions working directory
|
||||
preserve = self.options.get_config("preservedir") == "1"
|
||||
|
@ -1067,7 +1078,7 @@ class Session(object):
|
|||
"""
|
||||
try:
|
||||
state_file = open(self._state_file, "w")
|
||||
state_file.write("%d %s\n" % (state, coreapi.state_name(state)))
|
||||
state_file.write("%d %s\n" % (state, EventTypes(self.state).name))
|
||||
state_file.close()
|
||||
except IOError:
|
||||
logging.exception("error writing state file: %s", state)
|
||||
|
@ -1185,7 +1196,7 @@ class Session(object):
|
|||
hook(state)
|
||||
except Exception:
|
||||
message = "exception occured when running %s state hook: %s" % (
|
||||
coreapi.state_name(state),
|
||||
EventTypes(self.state).name,
|
||||
hook,
|
||||
)
|
||||
logging.exception(message)
|
||||
|
@ -1456,11 +1467,13 @@ class Session(object):
|
|||
# write current nodes out to session directory file
|
||||
self.write_nodes()
|
||||
|
||||
# create control net interfaces and broker network tunnels
|
||||
# create control net interfaces and network tunnels
|
||||
# which need to exist for emane to sync on location events
|
||||
# in distributed scenarios
|
||||
self.add_remove_control_interface(node=None, remove=False)
|
||||
self.broker.startup()
|
||||
|
||||
# initialize distributed tunnels
|
||||
self.distributed.start()
|
||||
|
||||
# instantiate will be invoked again upon Emane configure
|
||||
if self.emane.startup() == self.emane.NOT_READY:
|
||||
|
@ -1470,9 +1483,6 @@ class Session(object):
|
|||
self.boot_nodes()
|
||||
self.mobility.startup()
|
||||
|
||||
# set broker local instantiation to complete
|
||||
self.broker.local_instantiation_complete()
|
||||
|
||||
# notify listeners that instantiation is complete
|
||||
event = EventData(event_type=EventTypes.INSTANTIATION_COMPLETE.value)
|
||||
self.broadcast_event(event)
|
||||
|
@ -1510,21 +1520,16 @@ class Session(object):
|
|||
have entered runtime (time=0).
|
||||
"""
|
||||
# this is called from instantiate() after receiving an event message
|
||||
# for the instantiation state, and from the broker when distributed
|
||||
# nodes have been started
|
||||
# for the instantiation state
|
||||
logging.debug(
|
||||
"session(%s) checking if not in runtime state, current state: %s",
|
||||
self.id,
|
||||
coreapi.state_name(self.state),
|
||||
EventTypes(self.state).name,
|
||||
)
|
||||
if self.state == EventTypes.RUNTIME_STATE.value:
|
||||
logging.info("valid runtime state found, returning")
|
||||
return
|
||||
|
||||
# check to verify that all nodes and networks are running
|
||||
if not self.broker.instantiation_complete():
|
||||
return
|
||||
|
||||
# start event loop and set to runtime
|
||||
self.event_loop.run()
|
||||
self.set_state(EventTypes.RUNTIME_STATE, send_event=True)
|
||||
|
@ -1734,42 +1739,23 @@ class Session(object):
|
|||
except IndexError:
|
||||
# no server name. possibly only one server
|
||||
prefix = prefixes[0]
|
||||
else:
|
||||
# slave servers have their name and localhost in the serverlist
|
||||
servers = self.broker.getservernames()
|
||||
servers.remove("localhost")
|
||||
prefix = None
|
||||
|
||||
for server_prefix in prefixes:
|
||||
try:
|
||||
# split each entry into server and prefix
|
||||
server, p = server_prefix.split(":")
|
||||
except ValueError:
|
||||
server = ""
|
||||
p = None
|
||||
|
||||
if server == servers[0]:
|
||||
# the server name in the list matches this server
|
||||
prefix = p
|
||||
break
|
||||
|
||||
if not prefix:
|
||||
logging.error(
|
||||
"control network prefix not found for server: %s", servers[0]
|
||||
)
|
||||
assign_address = False
|
||||
try:
|
||||
prefix = prefixes[0].split(":", 1)[1]
|
||||
except IndexError:
|
||||
prefix = prefixes[0]
|
||||
# len(prefixes) == 1
|
||||
else:
|
||||
# TODO: can we get the server name from the servers.conf or from the node assignments?
|
||||
# TODO: can we get the server name from the servers.conf or from the node
|
||||
# assignments?o
|
||||
# with one prefix, only master gets a ctrlnet address
|
||||
assign_address = self.master
|
||||
prefix = prefixes[0]
|
||||
|
||||
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
||||
logging.info(
|
||||
"controlnet(%s) prefix(%s) assign(%s) updown(%s) serverintf(%s)",
|
||||
_id,
|
||||
prefix,
|
||||
assign_address,
|
||||
updown_script,
|
||||
server_interface,
|
||||
)
|
||||
control_net = self.create_node(
|
||||
cls=CtrlNet,
|
||||
_id=_id,
|
||||
|
@ -1779,13 +1765,6 @@ class Session(object):
|
|||
serverintf=server_interface,
|
||||
)
|
||||
|
||||
# tunnels between controlnets will be built with Broker.addnettunnels()
|
||||
# TODO: potentially remove documentation saying node ids are ints
|
||||
# TODO: need to move broker code out of the session object
|
||||
self.broker.addnet(_id)
|
||||
for server in self.broker.getservers():
|
||||
self.broker.addnodemap(server, _id)
|
||||
|
||||
return control_net
|
||||
|
||||
def add_remove_control_interface(
|
||||
|
@ -1918,7 +1897,8 @@ class Session(object):
|
|||
data,
|
||||
)
|
||||
|
||||
# TODO: if data is None, this blows up, but this ties into how event functions are ran, need to clean that up
|
||||
# TODO: if data is None, this blows up, but this ties into how event functions
|
||||
# are ran, need to clean that up
|
||||
def run_event(self, node_id=None, name=None, data=None):
|
||||
"""
|
||||
Run a scheduled event, executing commands in the data string.
|
||||
|
@ -1937,4 +1917,4 @@ class Session(object):
|
|||
utils.mute_detach(data)
|
||||
else:
|
||||
node = self.get_node(node_id)
|
||||
node.cmd(data, wait=False)
|
||||
node.node_net_cmd(data, wait=False)
|
||||
|
|
|
@ -10,7 +10,12 @@ class CoreCommandError(subprocess.CalledProcessError):
|
|||
"""
|
||||
|
||||
def __str__(self):
|
||||
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
|
||||
return "Command(%s), Status(%s):\nstdout: %s\nstderr: %s" % (
|
||||
self.cmd,
|
||||
self.returncode,
|
||||
self.output,
|
||||
self.stderr,
|
||||
)
|
||||
|
||||
|
||||
class CoreError(Exception):
|
||||
|
|
|
@ -19,13 +19,9 @@ from core.emulator.enumerations import (
|
|||
EventTypes,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTlvs,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.errors import CoreError
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
|
||||
|
||||
class MobilityManager(ModelManager):
|
||||
|
@ -48,11 +44,6 @@ class MobilityManager(ModelManager):
|
|||
self.models[BasicRangeModel.name] = BasicRangeModel
|
||||
self.models[Ns2ScriptedMobility.name] = Ns2ScriptedMobility
|
||||
|
||||
# dummy node objects for tracking position of nodes on other servers
|
||||
self.phys = {}
|
||||
self.physnets = {}
|
||||
self.session.broker.handlers.add(self.physnodehandlelink)
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Clear out all current configurations.
|
||||
|
@ -93,9 +84,6 @@ class MobilityManager(ModelManager):
|
|||
model_class = self.models[model_name]
|
||||
self.set_model(node, model_class, config)
|
||||
|
||||
if self.session.master:
|
||||
self.installphysnodes(node)
|
||||
|
||||
if node.mobility:
|
||||
self.session.event_loop.add_event(0.0, node.mobility.startup)
|
||||
|
||||
|
@ -209,87 +197,6 @@ class MobilityManager(ModelManager):
|
|||
if node.model:
|
||||
node.model.update(moved, moved_netifs)
|
||||
|
||||
def addphys(self, netnum, node):
|
||||
"""
|
||||
Keep track of PhysicalNodes and which network they belong to.
|
||||
|
||||
:param int netnum: network number
|
||||
:param core.coreobj.PyCoreNode node: node to add physical network to
|
||||
:return: nothing
|
||||
"""
|
||||
node_id = node.id
|
||||
self.phys[node_id] = node
|
||||
if netnum not in self.physnets:
|
||||
self.physnets[netnum] = [node_id]
|
||||
else:
|
||||
self.physnets[netnum].append(node_id)
|
||||
|
||||
# TODO: remove need for handling old style message
|
||||
|
||||
def physnodehandlelink(self, message):
|
||||
"""
|
||||
Broker handler. Snoop Link add messages to get
|
||||
node numbers of PhyiscalNodes and their nets.
|
||||
Physical nodes exist only on other servers, but a shadow object is
|
||||
created here for tracking node position.
|
||||
|
||||
:param message: link message to handle
|
||||
:return: nothing
|
||||
"""
|
||||
if (
|
||||
message.message_type == MessageTypes.LINK.value
|
||||
and message.flags & MessageFlags.ADD.value
|
||||
):
|
||||
nn = message.node_numbers()
|
||||
# first node is always link layer node in Link add message
|
||||
if nn[0] not in self.session.broker.network_nodes:
|
||||
return
|
||||
if nn[1] in self.session.broker.physical_nodes:
|
||||
# record the fact that this PhysicalNode is linked to a net
|
||||
dummy = CoreNodeBase(
|
||||
session=self.session, _id=nn[1], name="n%d" % nn[1], start=False
|
||||
)
|
||||
self.addphys(nn[0], dummy)
|
||||
|
||||
# TODO: remove need to handling old style messages
|
||||
def physnodeupdateposition(self, message):
|
||||
"""
|
||||
Snoop node messages belonging to physical nodes. The dummy object
|
||||
in self.phys[] records the node position.
|
||||
|
||||
:param message: message to handle
|
||||
:return: nothing
|
||||
"""
|
||||
nodenum = message.node_numbers()[0]
|
||||
try:
|
||||
dummy = self.phys[nodenum]
|
||||
nodexpos = message.get_tlv(NodeTlvs.X_POSITION.value)
|
||||
nodeypos = message.get_tlv(NodeTlvs.Y_POSITION.value)
|
||||
dummy.setposition(nodexpos, nodeypos, None)
|
||||
except KeyError:
|
||||
logging.exception("error retrieving physical node: %s", nodenum)
|
||||
|
||||
def installphysnodes(self, net):
|
||||
"""
|
||||
After installing a mobility model on a net, include any physical
|
||||
nodes that we have recorded. Use the GreTap tunnel to the physical node
|
||||
as the node's interface.
|
||||
|
||||
:param net: network to install
|
||||
:return: nothing
|
||||
"""
|
||||
node_ids = self.physnets.get(net.id, [])
|
||||
for node_id in node_ids:
|
||||
node = self.phys[node_id]
|
||||
# TODO: fix this bad logic, relating to depending on a break to get a valid server
|
||||
for server in self.session.broker.getserversbynode(node_id):
|
||||
break
|
||||
netif = self.session.broker.gettunnel(net.id, IpAddress.to_int(server.host))
|
||||
node.addnetif(netif, 0)
|
||||
netif.node = node
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
|
||||
|
||||
class WirelessModel(ConfigurableOptions):
|
||||
"""
|
||||
|
@ -1281,7 +1188,7 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
if filename is None or filename == "":
|
||||
return
|
||||
filename = self.findfile(filename)
|
||||
args = ["/bin/sh", filename, typestr]
|
||||
args = "/bin/sh %s %s" % (filename, typestr)
|
||||
utils.check_cmd(
|
||||
args, cwd=self.session.session_dir, env=self.session.get_environment()
|
||||
)
|
||||
|
|
|
@ -2,24 +2,21 @@
|
|||
Defines the base logic for nodes used within core.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import signal
|
||||
import socket
|
||||
import string
|
||||
import threading
|
||||
from builtins import range
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from core import constants, utils
|
||||
from core import utils
|
||||
from core.constants import MOUNT_BIN, VNODED_BIN
|
||||
from core.emulator.data import LinkData, NodeData
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.errors import CoreCommandError
|
||||
from core.nodes import client, ipaddress
|
||||
from core.nodes.interface import CoreInterface, TunTap, Veth
|
||||
from core.nodes.netclient import LinuxNetClient, OvsNetClient
|
||||
from core.nodes.interface import TunTap, Veth
|
||||
from core.nodes.netclient import get_net_client
|
||||
|
||||
_DEFAULT_MTU = 1500
|
||||
|
||||
|
@ -32,7 +29,7 @@ class NodeBase(object):
|
|||
apitype = None
|
||||
|
||||
# TODO: appears start has no usage, verify and remove
|
||||
def __init__(self, session, _id=None, name=None, start=True):
|
||||
def __init__(self, session, _id=None, name=None, start=True, server=None):
|
||||
"""
|
||||
Creates a PyCoreObj instance.
|
||||
|
||||
|
@ -40,7 +37,8 @@ class NodeBase(object):
|
|||
:param int _id: id
|
||||
:param str name: object name
|
||||
:param bool start: start value
|
||||
:return:
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
|
||||
self.session = session
|
||||
|
@ -50,8 +48,9 @@ class NodeBase(object):
|
|||
if name is None:
|
||||
name = "o%s" % self.id
|
||||
self.name = name
|
||||
self.server = server
|
||||
|
||||
self.type = None
|
||||
self.server = None
|
||||
self.services = None
|
||||
# ifindex is key, CoreInterface instance is value
|
||||
self._netif = {}
|
||||
|
@ -61,10 +60,8 @@ class NodeBase(object):
|
|||
self.opaque = None
|
||||
self.position = Position()
|
||||
|
||||
if session.options.get_config("ovs") == "True":
|
||||
self.net_client = OvsNetClient(self.net_cmd)
|
||||
else:
|
||||
self.net_client = LinuxNetClient(self.net_cmd)
|
||||
use_ovs = session.options.get_config("ovs") == "True"
|
||||
self.net_client = get_net_client(use_ovs, self.net_cmd)
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
|
@ -82,17 +79,23 @@ class NodeBase(object):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def net_cmd(self, args):
|
||||
def net_cmd(self, args, env=None, cwd=None, wait=True):
|
||||
"""
|
||||
Runs a command that is used to configure and setup the network on the host
|
||||
system.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param str args: command to run
|
||||
:param dict env: environment to run command with
|
||||
:param str cwd: directory to run command in
|
||||
:param bool wait: True to wait for status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
return utils.check_cmd(args)
|
||||
if self.server is None:
|
||||
return utils.check_cmd(args, env, cwd, wait)
|
||||
else:
|
||||
return self.server.remote_cmd(args, env, cwd, wait)
|
||||
|
||||
def setposition(self, x=None, y=None, z=None):
|
||||
"""
|
||||
|
@ -191,7 +194,9 @@ class NodeBase(object):
|
|||
|
||||
x, y, _ = self.getposition()
|
||||
model = self.type
|
||||
emulation_server = self.server
|
||||
emulation_server = None
|
||||
if self.server is not None:
|
||||
emulation_server = self.server.host
|
||||
|
||||
services = self.services
|
||||
if services is not None:
|
||||
|
@ -236,7 +241,7 @@ class CoreNodeBase(NodeBase):
|
|||
Base class for CORE nodes.
|
||||
"""
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True):
|
||||
def __init__(self, session, _id=None, name=None, start=True, server=None):
|
||||
"""
|
||||
Create a CoreNodeBase instance.
|
||||
|
||||
|
@ -244,8 +249,10 @@ class CoreNodeBase(NodeBase):
|
|||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: boolean for starting
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
super(CoreNodeBase, self).__init__(session, _id, name, start=start)
|
||||
super(CoreNodeBase, self).__init__(session, _id, name, start, server)
|
||||
self.services = []
|
||||
self.nodedir = None
|
||||
self.tmpnodedir = False
|
||||
|
@ -258,7 +265,7 @@ class CoreNodeBase(NodeBase):
|
|||
"""
|
||||
if self.nodedir is None:
|
||||
self.nodedir = os.path.join(self.session.session_dir, self.name + ".conf")
|
||||
os.makedirs(self.nodedir)
|
||||
self.net_cmd("mkdir -p %s" % self.nodedir)
|
||||
self.tmpnodedir = True
|
||||
else:
|
||||
self.tmpnodedir = False
|
||||
|
@ -274,7 +281,7 @@ class CoreNodeBase(NodeBase):
|
|||
return
|
||||
|
||||
if self.tmpnodedir:
|
||||
shutil.rmtree(self.nodedir, ignore_errors=True)
|
||||
self.net_cmd("rm -rf %s" % self.nodedir)
|
||||
|
||||
def addnetif(self, netif, ifindex):
|
||||
"""
|
||||
|
@ -323,7 +330,7 @@ class CoreNodeBase(NodeBase):
|
|||
Attach a network.
|
||||
|
||||
:param int ifindex: interface of index to attach
|
||||
:param core.nodes.interface.CoreInterface net: network to attach
|
||||
:param core.nodes.base.CoreNetworkBase net: network to attach
|
||||
:return: nothing
|
||||
"""
|
||||
if ifindex not in self._netif:
|
||||
|
@ -376,50 +383,19 @@ class CoreNodeBase(NodeBase):
|
|||
|
||||
return common
|
||||
|
||||
def node_net_cmd(self, args):
|
||||
def node_net_cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs a command that is used to configure and setup the network within a
|
||||
node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param str args: command to run
|
||||
:param bool wait: True to wait for status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_cmd(self, args):
|
||||
"""
|
||||
Runs shell command on node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs shell command on node, with option to not wait for a result.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param bool wait: wait for command to exit, defaults to True
|
||||
:return: exit status for command
|
||||
:rtype: int
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Runs shell command on node and get exit status and output.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exit status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def termcmdstring(self, sh):
|
||||
"""
|
||||
Create a terminal command string.
|
||||
|
@ -439,7 +415,14 @@ class CoreNode(CoreNodeBase):
|
|||
valid_address_types = {"inet", "inet6", "inet6link"}
|
||||
|
||||
def __init__(
|
||||
self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True
|
||||
self,
|
||||
session,
|
||||
_id=None,
|
||||
name=None,
|
||||
nodedir=None,
|
||||
bootsh="boot.sh",
|
||||
start=True,
|
||||
server=None,
|
||||
):
|
||||
"""
|
||||
Create a CoreNode instance.
|
||||
|
@ -450,8 +433,10 @@ class CoreNode(CoreNodeBase):
|
|||
:param str nodedir: node directory
|
||||
:param str bootsh: boot shell to use
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
super(CoreNode, self).__init__(session, _id, name, start)
|
||||
super(CoreNode, self).__init__(session, _id, name, start, server)
|
||||
self.nodedir = nodedir
|
||||
self.ctrlchnlname = os.path.abspath(
|
||||
os.path.join(self.session.session_dir, self.name)
|
||||
|
@ -463,14 +448,22 @@ class CoreNode(CoreNodeBase):
|
|||
self._mounts = []
|
||||
self.bootsh = bootsh
|
||||
|
||||
if session.options.get_config("ovs") == "True":
|
||||
self.node_net_client = OvsNetClient(self.node_net_cmd)
|
||||
else:
|
||||
self.node_net_client = LinuxNetClient(self.node_net_cmd)
|
||||
use_ovs = session.options.get_config("ovs") == "True"
|
||||
self.node_net_client = self.create_node_net_client(use_ovs)
|
||||
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
def create_node_net_client(self, use_ovs):
|
||||
"""
|
||||
Create node network client for running network commands within the nodes
|
||||
container.
|
||||
|
||||
:param bool use_ovs: True for OVS bridges, False for Linux bridges
|
||||
:return:node network client
|
||||
"""
|
||||
return get_net_client(use_ovs, self.node_net_cmd)
|
||||
|
||||
def alive(self):
|
||||
"""
|
||||
Check if the node is alive.
|
||||
|
@ -479,8 +472,8 @@ class CoreNode(CoreNodeBase):
|
|||
:rtype: bool
|
||||
"""
|
||||
try:
|
||||
os.kill(self.pid, 0)
|
||||
except OSError:
|
||||
self.net_cmd("kill -0 %s" % self.pid)
|
||||
except CoreCommandError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -499,24 +492,18 @@ class CoreNode(CoreNodeBase):
|
|||
raise ValueError("starting a node that is already up")
|
||||
|
||||
# create a new namespace for this node using vnoded
|
||||
vnoded = [
|
||||
constants.VNODED_BIN,
|
||||
"-v",
|
||||
"-c",
|
||||
self.ctrlchnlname,
|
||||
"-l",
|
||||
self.ctrlchnlname + ".log",
|
||||
"-p",
|
||||
self.ctrlchnlname + ".pid",
|
||||
]
|
||||
vnoded = "{cmd} -v -c {name} -l {name}.log -p {name}.pid".format(
|
||||
cmd=VNODED_BIN, name=self.ctrlchnlname
|
||||
)
|
||||
if self.nodedir:
|
||||
vnoded += ["-C", self.nodedir]
|
||||
vnoded += " -C %s" % self.nodedir
|
||||
env = self.session.get_environment(state=False)
|
||||
env["NODE_NUMBER"] = str(self.id)
|
||||
env["NODE_NAME"] = str(self.name)
|
||||
|
||||
output = utils.check_cmd(vnoded, env=env)
|
||||
output = self.net_cmd(vnoded, env=env)
|
||||
self.pid = int(output)
|
||||
logging.debug("node(%s) pid: %s", self.name, self.pid)
|
||||
|
||||
# create vnode client
|
||||
self.client = client.VnodeClient(self.name, self.ctrlchnlname)
|
||||
|
@ -556,20 +543,16 @@ class CoreNode(CoreNodeBase):
|
|||
for netif in self.netifs():
|
||||
netif.shutdown()
|
||||
|
||||
# attempt to kill node process and wait for termination of children
|
||||
# kill node process if present
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGTERM)
|
||||
os.waitpid(self.pid, 0)
|
||||
except OSError as e:
|
||||
if e.errno != 10:
|
||||
self.net_cmd("kill -9 %s" % self.pid)
|
||||
except CoreCommandError:
|
||||
logging.exception("error killing process")
|
||||
|
||||
# remove node directory if present
|
||||
try:
|
||||
os.unlink(self.ctrlchnlname)
|
||||
except OSError as e:
|
||||
# no such file or directory
|
||||
if e.errno != errno.ENOENT:
|
||||
self.net_cmd("rm -rf %s" % self.ctrlchnlname)
|
||||
except CoreCommandError:
|
||||
logging.exception("error removing node directory")
|
||||
|
||||
# clear interface data, close client, and mark self and not up
|
||||
|
@ -581,49 +564,22 @@ class CoreNode(CoreNodeBase):
|
|||
finally:
|
||||
self.rmnodedir()
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs shell command on node, with option to not wait for a result.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param bool wait: wait for command to exit, defaults to True
|
||||
:return: exit status for command
|
||||
:rtype: int
|
||||
"""
|
||||
return self.client.cmd(args, wait)
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Runs shell command on node and get exit status and output.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exit status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
return self.client.cmd_output(args)
|
||||
|
||||
def node_net_cmd(self, args):
|
||||
def node_net_cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs a command that is used to configure and setup the network within a
|
||||
node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param str args: command to run
|
||||
:param bool wait: True to wait for status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
return self.check_cmd(args)
|
||||
|
||||
def check_cmd(self, args):
|
||||
"""
|
||||
Runs shell command on node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
return self.client.check_cmd(args)
|
||||
if self.server is None:
|
||||
return self.client.check_cmd(args, wait=wait)
|
||||
else:
|
||||
args = self.client.create_cmd(args)
|
||||
return self.server.remote_cmd(args, wait=wait)
|
||||
|
||||
def termcmdstring(self, sh="/bin/sh"):
|
||||
"""
|
||||
|
@ -632,7 +588,13 @@ class CoreNode(CoreNodeBase):
|
|||
:param str sh: shell to execute command in
|
||||
:return: str
|
||||
"""
|
||||
return self.client.termcmdstring(sh)
|
||||
terminal = self.client.create_cmd(sh)
|
||||
if self.server is None:
|
||||
return terminal
|
||||
else:
|
||||
return "ssh -X -f {host} xterm -e {terminal}".format(
|
||||
host=self.server.host, terminal=terminal
|
||||
)
|
||||
|
||||
def privatedir(self, path):
|
||||
"""
|
||||
|
@ -646,7 +608,7 @@ class CoreNode(CoreNodeBase):
|
|||
hostpath = os.path.join(
|
||||
self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")
|
||||
)
|
||||
os.mkdir(hostpath)
|
||||
self.net_cmd("mkdir -p %s" % hostpath)
|
||||
self.mount(hostpath, path)
|
||||
|
||||
def mount(self, source, target):
|
||||
|
@ -660,8 +622,8 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
source = os.path.abspath(source)
|
||||
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
|
||||
self.client.check_cmd(["mkdir", "-p", target])
|
||||
self.client.check_cmd([constants.MOUNT_BIN, "-n", "--bind", source, target])
|
||||
self.node_net_cmd("mkdir -p %s" % target)
|
||||
self.node_net_cmd("%s -n --bind %s %s" % (MOUNT_BIN, source, target))
|
||||
self._mounts.append((source, target))
|
||||
|
||||
def newifindex(self):
|
||||
|
@ -706,7 +668,7 @@ class CoreNode(CoreNodeBase):
|
|||
raise ValueError("interface name (%s) too long" % name)
|
||||
|
||||
veth = Veth(
|
||||
node=self, name=name, localname=localname, net=net, start=self.up
|
||||
self.session, self, name, localname, start=self.up, server=self.server
|
||||
)
|
||||
|
||||
if self.up:
|
||||
|
@ -759,9 +721,7 @@ class CoreNode(CoreNodeBase):
|
|||
sessionid = self.session.short_session_id()
|
||||
localname = "tap%s.%s.%s" % (self.id, ifindex, sessionid)
|
||||
name = ifname
|
||||
tuntap = TunTap(
|
||||
node=self, name=name, localname=localname, net=net, start=self.up
|
||||
)
|
||||
tuntap = TunTap(self.session, self, name, localname, start=self.up)
|
||||
|
||||
try:
|
||||
self.addnetif(tuntap, ifindex)
|
||||
|
@ -878,35 +838,6 @@ class CoreNode(CoreNodeBase):
|
|||
self.ifup(ifindex)
|
||||
return ifindex
|
||||
|
||||
def connectnode(self, ifname, othernode, otherifname):
|
||||
"""
|
||||
Connect a node.
|
||||
|
||||
:param str ifname: name of interface to connect
|
||||
:param core.nodes.base.CoreNode othernode: node to connect to
|
||||
:param str otherifname: interface name to connect to
|
||||
:return: nothing
|
||||
"""
|
||||
tmplen = 8
|
||||
tmp1 = "tmp." + "".join(
|
||||
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||
)
|
||||
tmp2 = "tmp." + "".join(
|
||||
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||
)
|
||||
self.net_client.create_veth(tmp1, tmp2)
|
||||
self.net_client.device_ns(tmp1, str(self.pid))
|
||||
self.node_net_client.device_name(tmp1, ifname)
|
||||
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
|
||||
self.addnetif(interface, self.newifindex())
|
||||
|
||||
self.net_client.device_ns(tmp2, str(othernode.pid))
|
||||
othernode.node_net_client.device_name(tmp2, otherifname)
|
||||
other_interface = CoreInterface(
|
||||
node=othernode, name=otherifname, mtu=_DEFAULT_MTU
|
||||
)
|
||||
othernode.addnetif(other_interface, othernode.newifindex())
|
||||
|
||||
def addfile(self, srcname, filename):
|
||||
"""
|
||||
Add a file.
|
||||
|
@ -918,9 +849,13 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
logging.info("adding file from %s to %s", srcname, filename)
|
||||
directory = os.path.dirname(filename)
|
||||
self.client.check_cmd(["mkdir", "-p", directory])
|
||||
self.client.check_cmd(["mv", srcname, filename])
|
||||
self.client.check_cmd(["sync"])
|
||||
if self.server is None:
|
||||
self.client.check_cmd("mkdir -p %s" % directory)
|
||||
self.client.check_cmd("mv %s %s" % (srcname, filename))
|
||||
self.client.check_cmd("sync")
|
||||
else:
|
||||
self.net_cmd("mkdir -p %s" % directory)
|
||||
self.server.remote_put(srcname, filename)
|
||||
|
||||
def hostfilename(self, filename):
|
||||
"""
|
||||
|
@ -938,35 +873,29 @@ class CoreNode(CoreNodeBase):
|
|||
dirname = os.path.join(self.nodedir, dirname)
|
||||
return os.path.join(dirname, basename)
|
||||
|
||||
def opennodefile(self, filename, mode="w"):
|
||||
"""
|
||||
Open a node file, within it"s directory.
|
||||
|
||||
:param str filename: file name to open
|
||||
:param str mode: mode to open file in
|
||||
:return: open file
|
||||
:rtype: file
|
||||
"""
|
||||
hostfilename = self.hostfilename(filename)
|
||||
dirname, _basename = os.path.split(hostfilename)
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname, mode=0o755)
|
||||
return open(hostfilename, mode)
|
||||
|
||||
def nodefile(self, filename, contents, mode=0o644):
|
||||
"""
|
||||
Create a node file with a given mode.
|
||||
|
||||
:param str filename: name of file to create
|
||||
:param contents: contents of file
|
||||
:param str contents: contents of file
|
||||
:param int mode: mode for file
|
||||
:return: nothing
|
||||
"""
|
||||
with self.opennodefile(filename, "w") as open_file:
|
||||
hostfilename = self.hostfilename(filename)
|
||||
dirname, _basename = os.path.split(hostfilename)
|
||||
if self.server is None:
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname, mode=0o755)
|
||||
with open(hostfilename, "w") as open_file:
|
||||
open_file.write(contents)
|
||||
os.chmod(open_file.name, mode)
|
||||
else:
|
||||
self.net_cmd("mkdir -m %o -p %s" % (0o755, dirname))
|
||||
self.server.remote_put_temp(hostfilename, contents)
|
||||
self.net_cmd("chmod %o %s" % (mode, hostfilename))
|
||||
logging.debug(
|
||||
"node(%s) added file: %s; mode: 0%o", self.name, open_file.name, mode
|
||||
"node(%s) added file: %s; mode: 0%o", self.name, hostfilename, mode
|
||||
)
|
||||
|
||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||
|
@ -980,9 +909,12 @@ class CoreNode(CoreNodeBase):
|
|||
:return: nothing
|
||||
"""
|
||||
hostfilename = self.hostfilename(filename)
|
||||
if self.server is None:
|
||||
shutil.copy2(srcfilename, hostfilename)
|
||||
else:
|
||||
self.server.remote_put(srcfilename, hostfilename)
|
||||
if mode is not None:
|
||||
os.chmod(hostfilename, mode)
|
||||
self.net_cmd("chmod %o %s" % (mode, hostfilename))
|
||||
logging.info(
|
||||
"node(%s) copied file: %s; mode: %s", self.name, hostfilename, mode
|
||||
)
|
||||
|
@ -996,7 +928,7 @@ class CoreNetworkBase(NodeBase):
|
|||
linktype = LinkTypes.WIRED.value
|
||||
is_emane = False
|
||||
|
||||
def __init__(self, session, _id, name, start=True):
|
||||
def __init__(self, session, _id, name, start=True, server=None):
|
||||
"""
|
||||
Create a CoreNetworkBase instance.
|
||||
|
||||
|
@ -1004,8 +936,10 @@ class CoreNetworkBase(NodeBase):
|
|||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: should object start
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
super(CoreNetworkBase, self).__init__(session, _id, name, start=start)
|
||||
super(CoreNetworkBase, self).__init__(session, _id, name, start, server)
|
||||
self._linked = {}
|
||||
self._linked_lock = threading.Lock()
|
||||
|
||||
|
|
|
@ -4,12 +4,8 @@ over a control channel to the vnoded process running in a network namespace.
|
|||
The control channel can be accessed via calls using the vcmd shell.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
from core import constants, utils
|
||||
from core.errors import CoreCommandError
|
||||
from core import utils
|
||||
from core.constants import VCMD_BIN
|
||||
|
||||
|
||||
class VnodeClient(object):
|
||||
|
@ -54,133 +50,19 @@ class VnodeClient(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def _cmd_args(self):
|
||||
return [constants.VCMD_BIN, "-c", self.ctrlchnlname, "--"]
|
||||
def create_cmd(self, args):
|
||||
return "%s -c %s -- %s" % (VCMD_BIN, self.ctrlchnlname, args)
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Execute a command on a node and return the status (return code).
|
||||
|
||||
:param list[str]|str args: command arguments
|
||||
:param bool wait: wait for command to end or not
|
||||
:return: command status
|
||||
:rtype: int
|
||||
"""
|
||||
self._verify_connection()
|
||||
args = utils.split_args(args)
|
||||
|
||||
# run command, return process when not waiting
|
||||
cmd = self._cmd_args() + args
|
||||
logging.debug("cmd wait(%s): %s", wait, cmd)
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
if not wait:
|
||||
return 0
|
||||
|
||||
# wait for and return exit status
|
||||
return p.wait()
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Execute a command on a node and return a tuple containing the
|
||||
exit status and result string. stderr output
|
||||
is folded into the stdout result string.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: command status and combined stdout and stderr output
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
p, stdin, stdout, stderr = self.popen(args)
|
||||
stdin.close()
|
||||
output = stdout.read() + stderr.read()
|
||||
stdout.close()
|
||||
stderr.close()
|
||||
status = p.wait()
|
||||
return status, output.decode("utf-8").strip()
|
||||
|
||||
def check_cmd(self, args):
|
||||
def check_cmd(self, args, wait=True):
|
||||
"""
|
||||
Run command and return exit status and combined stdout and stderr.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param str args: command to run
|
||||
:param bool wait: True to wait for command status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises core.CoreCommandError: when there is a non-zero exit status
|
||||
"""
|
||||
status, output = self.cmd_output(args)
|
||||
if status != 0:
|
||||
raise CoreCommandError(status, args, output)
|
||||
return output.strip()
|
||||
|
||||
def popen(self, args):
|
||||
"""
|
||||
Execute a popen command against the node.
|
||||
|
||||
:param list[str]|str args: command arguments
|
||||
:return: popen object, stdin, stdout, and stderr
|
||||
:rtype: tuple
|
||||
"""
|
||||
self._verify_connection()
|
||||
args = utils.split_args(args)
|
||||
cmd = self._cmd_args() + args
|
||||
logging.debug("popen: %s", cmd)
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
return p, p.stdin, p.stdout, p.stderr
|
||||
|
||||
def icmd(self, args):
|
||||
"""
|
||||
Execute an icmd against a node.
|
||||
|
||||
:param list[str]|str args: command arguments
|
||||
:return: command result
|
||||
:rtype: int
|
||||
"""
|
||||
args = utils.split_args(args)
|
||||
return os.spawnlp(
|
||||
os.P_WAIT,
|
||||
constants.VCMD_BIN,
|
||||
constants.VCMD_BIN,
|
||||
"-c",
|
||||
self.ctrlchnlname,
|
||||
"--",
|
||||
*args
|
||||
)
|
||||
|
||||
def term(self, sh="/bin/sh"):
|
||||
"""
|
||||
Open a terminal on a node.
|
||||
|
||||
:param str sh: shell to open terminal with
|
||||
:return: terminal command result
|
||||
:rtype: int
|
||||
"""
|
||||
args = (
|
||||
"xterm",
|
||||
"-ut",
|
||||
"-title",
|
||||
self.name,
|
||||
"-e",
|
||||
constants.VCMD_BIN,
|
||||
"-c",
|
||||
self.ctrlchnlname,
|
||||
"--",
|
||||
sh,
|
||||
)
|
||||
if "SUDO_USER" in os.environ:
|
||||
args = (
|
||||
"su",
|
||||
"-s",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"exec " + " ".join(map(lambda x: "'%s'" % x, args)),
|
||||
os.environ["SUDO_USER"],
|
||||
)
|
||||
return os.spawnvp(os.P_NOWAIT, args[0], args)
|
||||
|
||||
def termcmdstring(self, sh="/bin/sh"):
|
||||
"""
|
||||
Create a terminal command string.
|
||||
|
||||
:param str sh: shell to execute command in
|
||||
:return: str
|
||||
"""
|
||||
return "%s -c %s -- %s" % (constants.VCMD_BIN, self.ctrlchnlname, sh)
|
||||
args = self.create_cmd(args)
|
||||
return utils.check_cmd(args, wait=wait)
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from core import utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.errors import CoreCommandError
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.netclient import get_net_client
|
||||
|
||||
|
||||
class DockerClient(object):
|
||||
def __init__(self, name, image):
|
||||
def __init__(self, name, image, run):
|
||||
self.name = name
|
||||
self.image = image
|
||||
self.run = run
|
||||
self.pid = None
|
||||
|
||||
def create_container(self):
|
||||
utils.check_cmd(
|
||||
self.run(
|
||||
"docker run -td --init --net=none --hostname {name} --name {name} "
|
||||
"--sysctl net.ipv6.conf.all.disable_ipv6=0 "
|
||||
"{image} /bin/bash".format(
|
||||
|
@ -27,12 +30,12 @@ class DockerClient(object):
|
|||
|
||||
def get_info(self):
|
||||
args = "docker inspect {name}".format(name=self.name)
|
||||
status, output = utils.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
output = self.run(args)
|
||||
data = json.loads(output)
|
||||
if not data:
|
||||
raise CoreCommandError(status, args, "docker({name}) not present".format(name=self.name))
|
||||
raise CoreCommandError(
|
||||
-1, args, "docker({name}) not present".format(name=self.name)
|
||||
)
|
||||
return data[0]
|
||||
|
||||
def is_alive(self):
|
||||
|
@ -43,43 +46,33 @@ class DockerClient(object):
|
|||
return False
|
||||
|
||||
def stop_container(self):
|
||||
utils.check_cmd("docker rm -f {name}".format(
|
||||
self.run("docker rm -f {name}".format(
|
||||
name=self.name
|
||||
))
|
||||
|
||||
def cmd(self, cmd, wait=True):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
logging.info("docker cmd wait(%s): %s", wait, cmd)
|
||||
return utils.cmd("docker exec {name} {cmd}".format(
|
||||
name=self.name,
|
||||
cmd=cmd
|
||||
), wait)
|
||||
|
||||
def cmd_output(self, cmd):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
def check_cmd(self, cmd):
|
||||
logging.info("docker cmd output: %s", cmd)
|
||||
return utils.cmd_output("docker exec {name} {cmd}".format(
|
||||
return utils.check_cmd("docker exec {name} {cmd}".format(
|
||||
name=self.name,
|
||||
cmd=cmd
|
||||
))
|
||||
|
||||
def ns_cmd(self, cmd):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
def create_ns_cmd(self, cmd):
|
||||
return "nsenter -t {pid} -u -i -p -n {cmd}".format(
|
||||
pid=self.pid,
|
||||
cmd=cmd
|
||||
)
|
||||
|
||||
def ns_cmd(self, cmd, wait):
|
||||
args = "nsenter -t {pid} -u -i -p -n {cmd}".format(
|
||||
pid=self.pid,
|
||||
cmd=cmd
|
||||
)
|
||||
logging.info("ns cmd: %s", args)
|
||||
return utils.cmd_output(args)
|
||||
return utils.check_cmd(args, wait=wait)
|
||||
|
||||
def get_pid(self):
|
||||
args = "docker inspect -f '{{{{.State.Pid}}}}' {name}".format(name=self.name)
|
||||
status, output = utils.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
output = self.run(args)
|
||||
self.pid = output
|
||||
logging.debug("node(%s) pid: %s", self.name, self.pid)
|
||||
return output
|
||||
|
@ -90,15 +83,23 @@ class DockerClient(object):
|
|||
name=self.name,
|
||||
destination=destination
|
||||
)
|
||||
status, output = utils.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
return self.run(args)
|
||||
|
||||
|
||||
class DockerNode(CoreNode):
|
||||
apitype = NodeTypes.DOCKER.value
|
||||
|
||||
def __init__(self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True, image=None):
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
_id=None,
|
||||
name=None,
|
||||
nodedir=None,
|
||||
bootsh="boot.sh",
|
||||
start=True,
|
||||
server=None,
|
||||
image=None
|
||||
):
|
||||
"""
|
||||
Create a DockerNode instance.
|
||||
|
||||
|
@ -108,12 +109,26 @@ class DockerNode(CoreNode):
|
|||
:param str nodedir: node directory
|
||||
:param str bootsh: boot shell to use
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param str image: image to start container with
|
||||
"""
|
||||
if image is None:
|
||||
image = "ubuntu"
|
||||
self.image = image
|
||||
super(DockerNode, self).__init__(session, _id, name, nodedir, bootsh, start)
|
||||
super(DockerNode, self).__init__(
|
||||
session, _id, name, nodedir, bootsh, start, server
|
||||
)
|
||||
|
||||
def create_node_net_client(self, use_ovs):
|
||||
"""
|
||||
Create node network client for running network commands within the nodes
|
||||
container.
|
||||
|
||||
:param bool use_ovs: True for OVS bridges, False for Linux bridges
|
||||
:return:node network client
|
||||
"""
|
||||
return get_net_client(use_ovs, self.nsenter_cmd)
|
||||
|
||||
def alive(self):
|
||||
"""
|
||||
|
@ -136,7 +151,7 @@ class DockerNode(CoreNode):
|
|||
if self.up:
|
||||
raise ValueError("starting a node that is already up")
|
||||
self.makenodedir()
|
||||
self.client = DockerClient(self.name, self.image)
|
||||
self.client = DockerClient(self.name, self.image, self.net_cmd)
|
||||
self.pid = self.client.create_container()
|
||||
self.up = True
|
||||
|
||||
|
@ -155,50 +170,13 @@ class DockerNode(CoreNode):
|
|||
self.client.stop_container()
|
||||
self.up = False
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs shell command on node, with option to not wait for a result.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param bool wait: wait for command to exit, defaults to True
|
||||
:return: exit status for command
|
||||
:rtype: int
|
||||
"""
|
||||
return self.client.cmd(args, wait)
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Runs shell command on node and get exit status and output.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exit status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
return self.client.cmd_output(args)
|
||||
|
||||
def check_cmd(self, args):
|
||||
"""
|
||||
Runs shell command on node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
status, output = self.client.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
return output
|
||||
|
||||
def node_net_cmd(self, args):
|
||||
if not self.up:
|
||||
logging.debug("node down, not running network command: %s", args)
|
||||
return 0
|
||||
|
||||
status, output = self.client.ns_cmd(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
return output
|
||||
def nsenter_cmd(self, args, wait=True):
|
||||
if self.server is None:
|
||||
args = self.client.create_ns_cmd(args)
|
||||
return utils.check_cmd(args, wait=wait)
|
||||
else:
|
||||
args = self.client.create_ns_cmd(args)
|
||||
return self.server.remote_cmd(args, wait=wait)
|
||||
|
||||
def termcmdstring(self, sh="/bin/sh"):
|
||||
"""
|
||||
|
@ -218,7 +196,7 @@ class DockerNode(CoreNode):
|
|||
"""
|
||||
logging.debug("creating node dir: %s", path)
|
||||
args = "mkdir -p {path}".format(path=path)
|
||||
self.check_cmd(args)
|
||||
self.node_net_cmd(args)
|
||||
|
||||
def mount(self, source, target):
|
||||
"""
|
||||
|
@ -241,13 +219,24 @@ class DockerNode(CoreNode):
|
|||
:param int mode: mode for file
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
|
||||
logging.debug("nodefile filename(%s) mode(%s)", filename, mode)
|
||||
file_path = os.path.join(self.nodedir, filename)
|
||||
with open(file_path, "w") as f:
|
||||
os.chmod(f.name, mode)
|
||||
f.write(contents)
|
||||
self.client.copy_file(file_path, filename)
|
||||
directory = os.path.dirname(filename)
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
temp.write(contents.encode("utf-8"))
|
||||
temp.close()
|
||||
|
||||
if directory:
|
||||
self.node_net_cmd("mkdir -m %o -p %s" % (0o755, directory))
|
||||
if self.server is not None:
|
||||
self.server.remote_put(temp.name, temp.name)
|
||||
self.client.copy_file(temp.name, filename)
|
||||
self.node_net_cmd("chmod %o %s" % (mode, filename))
|
||||
if self.server is not None:
|
||||
self.net_cmd("rm -f %s" % temp.name)
|
||||
os.unlink(temp.name)
|
||||
logging.debug(
|
||||
"node(%s) added file: %s; mode: 0%o", self.name, filename, mode
|
||||
)
|
||||
|
||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||
"""
|
||||
|
@ -259,5 +248,18 @@ class DockerNode(CoreNode):
|
|||
:param int mode: mode to copy to
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode)
|
||||
raise Exception("not supported")
|
||||
logging.info(
|
||||
"node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode
|
||||
)
|
||||
directory = os.path.dirname(filename)
|
||||
self.node_net_cmd("mkdir -p %s" % directory)
|
||||
|
||||
if self.server is None:
|
||||
source = srcfilename
|
||||
else:
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
source = temp.name
|
||||
self.server.remote_put(source, temp.name)
|
||||
|
||||
self.client.copy_file(source, filename)
|
||||
self.node_net_cmd("chmod %o %s" % (mode, filename))
|
||||
|
|
|
@ -8,7 +8,7 @@ from builtins import int, range
|
|||
|
||||
from core import utils
|
||||
from core.errors import CoreCommandError
|
||||
from core.nodes.netclient import LinuxNetClient
|
||||
from core.nodes.netclient import get_net_client
|
||||
|
||||
|
||||
class CoreInterface(object):
|
||||
|
@ -16,15 +16,18 @@ class CoreInterface(object):
|
|||
Base class for network interfaces.
|
||||
"""
|
||||
|
||||
def __init__(self, node, name, mtu):
|
||||
def __init__(self, session, node, name, mtu, server=None):
|
||||
"""
|
||||
Creates a PyCoreNetIf instance.
|
||||
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param core.nodes.base.CoreNode node: node for interface
|
||||
:param str name: interface name
|
||||
:param mtu: mtu value
|
||||
:param int mtu: mtu value
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
|
||||
self.session = session
|
||||
self.node = node
|
||||
self.name = name
|
||||
if not isinstance(mtu, int):
|
||||
|
@ -42,7 +45,26 @@ class CoreInterface(object):
|
|||
self.netindex = None
|
||||
# index used to find flow data
|
||||
self.flow_id = None
|
||||
self.net_client = LinuxNetClient(utils.check_cmd)
|
||||
self.server = server
|
||||
use_ovs = session.options.get_config("ovs") == "True"
|
||||
self.net_client = get_net_client(use_ovs, self.net_cmd)
|
||||
|
||||
def net_cmd(self, args, env=None, cwd=None, wait=True):
|
||||
"""
|
||||
Runs a command on the host system or distributed servers.
|
||||
|
||||
:param str args: command to run
|
||||
:param dict env: environment to run command with
|
||||
:param str cwd: directory to run command in
|
||||
:param bool wait: True to wait for status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
if self.server is None:
|
||||
return utils.check_cmd(args, env, cwd, wait)
|
||||
else:
|
||||
return self.server.remote_cmd(args, env, cwd, wait)
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
|
@ -191,21 +213,24 @@ class Veth(CoreInterface):
|
|||
Provides virtual ethernet functionality for core nodes.
|
||||
"""
|
||||
|
||||
# TODO: network is not used, why was it needed?
|
||||
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
|
||||
def __init__(
|
||||
self, session, node, name, localname, mtu=1500, server=None, start=True
|
||||
):
|
||||
"""
|
||||
Creates a VEth instance.
|
||||
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param core.nodes.base.CoreNode node: related core node
|
||||
:param str name: interface name
|
||||
:param str localname: interface local name
|
||||
:param mtu: interface mtu
|
||||
:param net: network
|
||||
:param int mtu: interface mtu
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param bool start: start flag
|
||||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
# note that net arg is ignored
|
||||
CoreInterface.__init__(self, node=node, name=name, mtu=mtu)
|
||||
CoreInterface.__init__(self, session, node, name, mtu, server)
|
||||
self.localname = localname
|
||||
self.up = False
|
||||
if start:
|
||||
|
@ -251,19 +276,22 @@ class TunTap(CoreInterface):
|
|||
TUN/TAP virtual device in TAP mode
|
||||
"""
|
||||
|
||||
# TODO: network is not used, why was it needed?
|
||||
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
|
||||
def __init__(
|
||||
self, session, node, name, localname, mtu=1500, server=None, start=True
|
||||
):
|
||||
"""
|
||||
Create a TunTap instance.
|
||||
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param core.nodes.base.CoreNode node: related core node
|
||||
:param str name: interface name
|
||||
:param str localname: local interface name
|
||||
:param mtu: interface mtu
|
||||
:param core.nodes.base.CoreNetworkBase net: related network
|
||||
:param int mtu: interface mtu
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param bool start: start flag
|
||||
"""
|
||||
CoreInterface.__init__(self, node=node, name=name, mtu=mtu)
|
||||
CoreInterface.__init__(self, session, node, name, mtu, server)
|
||||
self.localname = localname
|
||||
self.up = False
|
||||
self.transport_type = "virtual"
|
||||
|
@ -427,6 +455,7 @@ class GreTap(CoreInterface):
|
|||
ttl=255,
|
||||
key=None,
|
||||
start=True,
|
||||
server=None,
|
||||
):
|
||||
"""
|
||||
Creates a GreTap instance.
|
||||
|
@ -434,17 +463,18 @@ class GreTap(CoreInterface):
|
|||
:param core.nodes.base.CoreNode node: related core node
|
||||
:param str name: interface name
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param mtu: interface mtu
|
||||
:param int mtu: interface mtu
|
||||
:param str remoteip: remote address
|
||||
:param int _id: object id
|
||||
:param str localip: local address
|
||||
:param ttl: ttl value
|
||||
:param key: gre tap key
|
||||
:param int ttl: ttl value
|
||||
:param int key: gre tap key
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
CoreInterface.__init__(self, node=node, name=name, mtu=mtu)
|
||||
self.session = session
|
||||
CoreInterface.__init__(self, session, node, name, mtu, server)
|
||||
if _id is None:
|
||||
# from PyCoreObj
|
||||
_id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF
|
||||
|
@ -460,13 +490,7 @@ class GreTap(CoreInterface):
|
|||
if remoteip is None:
|
||||
raise ValueError("missing remote IP required for GRE TAP device")
|
||||
|
||||
if localip is not None:
|
||||
localip = str(localip)
|
||||
if ttl is not None:
|
||||
ttl = str(ttl)
|
||||
if key is not None:
|
||||
key = str(key)
|
||||
self.net_client.create_gretap(self.localname, str(remoteip), localip, ttl, key)
|
||||
self.net_client.create_gretap(self.localname, remoteip, localip, ttl, key)
|
||||
self.net_client.device_up(self.localname)
|
||||
self.up = True
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class MacAddress(object):
|
|||
"""
|
||||
Creates a MacAddress instance.
|
||||
|
||||
:param str address: mac address
|
||||
:param bytes address: mac address
|
||||
"""
|
||||
self.addr = address
|
||||
|
||||
|
@ -42,7 +42,7 @@ class MacAddress(object):
|
|||
"""
|
||||
if not self.addr:
|
||||
return IpAddress.from_string("::")
|
||||
tmp = struct.unpack("!Q", "\x00\x00" + self.addr)[0]
|
||||
tmp = struct.unpack("!Q", b"\x00\x00" + self.addr)[0]
|
||||
nic = int(tmp) & 0x000000FFFFFF
|
||||
oui = int(tmp) & 0xFFFFFF000000
|
||||
# toggle U/L bit
|
||||
|
@ -88,7 +88,7 @@ class IpAddress(object):
|
|||
Create a IpAddress instance.
|
||||
|
||||
:param int af: address family
|
||||
:param str address: ip address
|
||||
:param bytes address: ip address
|
||||
:return:
|
||||
"""
|
||||
# check if (af, addr) is valid
|
||||
|
|
|
@ -2,6 +2,7 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import time
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from core import utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
|
@ -10,28 +11,25 @@ from core.nodes.base import CoreNode
|
|||
|
||||
|
||||
class LxdClient(object):
|
||||
def __init__(self, name, image):
|
||||
def __init__(self, name, image, run):
|
||||
self.name = name
|
||||
self.image = image
|
||||
self.run = run
|
||||
self.pid = None
|
||||
|
||||
def create_container(self):
|
||||
utils.check_cmd(
|
||||
"lxc launch {image} {name}".format(name=self.name, image=self.image)
|
||||
)
|
||||
self.run("lxc launch {image} {name}".format(name=self.name, image=self.image))
|
||||
data = self.get_info()
|
||||
self.pid = data["state"]["pid"]
|
||||
return self.pid
|
||||
|
||||
def get_info(self):
|
||||
args = "lxc list {name} --format json".format(name=self.name)
|
||||
status, output = utils.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
output = self.run(args)
|
||||
data = json.loads(output)
|
||||
if not data:
|
||||
raise CoreCommandError(
|
||||
status, args, "LXC({name}) not present".format(name=self.name)
|
||||
-1, args, "LXC({name}) not present".format(name=self.name)
|
||||
)
|
||||
return data[0]
|
||||
|
||||
|
@ -43,41 +41,17 @@ class LxdClient(object):
|
|||
return False
|
||||
|
||||
def stop_container(self):
|
||||
utils.check_cmd("lxc delete --force {name}".format(name=self.name))
|
||||
self.run("lxc delete --force {name}".format(name=self.name))
|
||||
|
||||
def _cmd_args(self, cmd):
|
||||
def create_cmd(self, cmd):
|
||||
return "lxc exec -nT {name} -- {cmd}".format(name=self.name, cmd=cmd)
|
||||
|
||||
def cmd_output(self, cmd):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
args = self._cmd_args(cmd)
|
||||
logging.info("lxc cmd output: %s", args)
|
||||
return utils.cmd_output(args)
|
||||
|
||||
def cmd(self, cmd, wait=True):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
args = self._cmd_args(cmd)
|
||||
logging.info("lxc cmd: %s", args)
|
||||
return utils.cmd(args, wait)
|
||||
|
||||
def _ns_args(self, cmd):
|
||||
def create_ns_cmd(self, cmd):
|
||||
return "nsenter -t {pid} -m -u -i -p -n {cmd}".format(pid=self.pid, cmd=cmd)
|
||||
|
||||
def ns_cmd_output(self, cmd):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
args = self._ns_args(cmd)
|
||||
logging.info("ns cmd: %s", args)
|
||||
return utils.cmd_output(args)
|
||||
|
||||
def ns_cmd(self, cmd, wait=True):
|
||||
if isinstance(cmd, list):
|
||||
cmd = " ".join(cmd)
|
||||
args = self._ns_args(cmd)
|
||||
logging.info("ns cmd: %s", args)
|
||||
return utils.cmd(args, wait)
|
||||
def check_cmd(self, cmd, wait=True):
|
||||
args = self.create_cmd(cmd)
|
||||
return utils.check_cmd(args, wait=wait)
|
||||
|
||||
def copy_file(self, source, destination):
|
||||
if destination[0] != "/":
|
||||
|
@ -86,9 +60,7 @@ class LxdClient(object):
|
|||
args = "lxc file push {source} {name}/{destination}".format(
|
||||
source=source, name=self.name, destination=destination
|
||||
)
|
||||
status, output = utils.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
self.run(args)
|
||||
|
||||
|
||||
class LxcNode(CoreNode):
|
||||
|
@ -102,6 +74,7 @@ class LxcNode(CoreNode):
|
|||
nodedir=None,
|
||||
bootsh="boot.sh",
|
||||
start=True,
|
||||
server=None,
|
||||
image=None,
|
||||
):
|
||||
"""
|
||||
|
@ -113,12 +86,16 @@ class LxcNode(CoreNode):
|
|||
:param str nodedir: node directory
|
||||
:param str bootsh: boot shell to use
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param str image: image to start container with
|
||||
"""
|
||||
if image is None:
|
||||
image = "ubuntu"
|
||||
self.image = image
|
||||
super(LxcNode, self).__init__(session, _id, name, nodedir, bootsh, start)
|
||||
super(LxcNode, self).__init__(
|
||||
session, _id, name, nodedir, bootsh, start, server
|
||||
)
|
||||
|
||||
def alive(self):
|
||||
"""
|
||||
|
@ -139,7 +116,7 @@ class LxcNode(CoreNode):
|
|||
if self.up:
|
||||
raise ValueError("starting a node that is already up")
|
||||
self.makenodedir()
|
||||
self.client = LxdClient(self.name, self.image)
|
||||
self.client = LxdClient(self.name, self.image, self.net_cmd)
|
||||
self.pid = self.client.create_container()
|
||||
self.up = True
|
||||
|
||||
|
@ -158,47 +135,6 @@ class LxcNode(CoreNode):
|
|||
self.client.stop_container()
|
||||
self.up = False
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs shell command on node, with option to not wait for a result.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param bool wait: wait for command to exit, defaults to True
|
||||
:return: exit status for command
|
||||
:rtype: int
|
||||
"""
|
||||
return self.client.cmd(args, wait)
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Runs shell command on node and get exit status and output.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exit status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
return self.client.cmd_output(args)
|
||||
|
||||
def check_cmd(self, args):
|
||||
"""
|
||||
Runs shell command on node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
status, output = self.client.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
return output
|
||||
|
||||
def node_net_cmd(self, args):
|
||||
if not self.up:
|
||||
logging.debug("node down, not running network command: %s", args)
|
||||
return 0
|
||||
return self.check_cmd(args)
|
||||
|
||||
def termcmdstring(self, sh="/bin/sh"):
|
||||
"""
|
||||
Create a terminal command string.
|
||||
|
@ -206,7 +142,7 @@ class LxcNode(CoreNode):
|
|||
:param str sh: shell to execute command in
|
||||
:return: str
|
||||
"""
|
||||
return "lxc exec {name} -- bash".format(name=self.name)
|
||||
return "lxc exec {name} -- {sh}".format(name=self.name, sh=sh)
|
||||
|
||||
def privatedir(self, path):
|
||||
"""
|
||||
|
@ -217,7 +153,7 @@ class LxcNode(CoreNode):
|
|||
"""
|
||||
logging.info("creating node dir: %s", path)
|
||||
args = "mkdir -p {path}".format(path=path)
|
||||
self.check_cmd(args)
|
||||
return self.node_net_cmd(args)
|
||||
|
||||
def mount(self, source, target):
|
||||
"""
|
||||
|
@ -240,13 +176,23 @@ class LxcNode(CoreNode):
|
|||
:param int mode: mode for file
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
|
||||
logging.debug("nodefile filename(%s) mode(%s)", filename, mode)
|
||||
file_path = os.path.join(self.nodedir, filename)
|
||||
with open(file_path, "w") as f:
|
||||
os.chmod(f.name, mode)
|
||||
f.write(contents)
|
||||
self.client.copy_file(file_path, filename)
|
||||
|
||||
directory = os.path.dirname(filename)
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
temp.write(contents.encode("utf-8"))
|
||||
temp.close()
|
||||
|
||||
if directory:
|
||||
self.node_net_cmd("mkdir -m %o -p %s" % (0o755, directory))
|
||||
if self.server is not None:
|
||||
self.server.remote_put(temp.name, temp.name)
|
||||
self.client.copy_file(temp.name, filename)
|
||||
self.node_net_cmd("chmod %o %s" % (mode, filename))
|
||||
if self.server is not None:
|
||||
self.net_cmd("rm -f %s" % temp.name)
|
||||
os.unlink(temp.name)
|
||||
logging.debug("node(%s) added file: %s; mode: 0%o", self.name, filename, mode)
|
||||
|
||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||
"""
|
||||
|
@ -261,7 +207,18 @@ class LxcNode(CoreNode):
|
|||
logging.info(
|
||||
"node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode
|
||||
)
|
||||
raise Exception("not supported")
|
||||
directory = os.path.dirname(filename)
|
||||
self.node_net_cmd("mkdir -p %s" % directory)
|
||||
|
||||
if self.server is None:
|
||||
source = srcfilename
|
||||
else:
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
source = temp.name
|
||||
self.server.remote_put(source, temp.name)
|
||||
|
||||
self.client.copy_file(source, filename)
|
||||
self.node_net_cmd("chmod %o %s" % (mode, filename))
|
||||
|
||||
def addnetif(self, netif, ifindex):
|
||||
super(LxcNode, self).addnetif(netif, ifindex)
|
||||
|
|
|
@ -5,7 +5,20 @@ Clients for dealing with bridge/interface commands.
|
|||
import os
|
||||
|
||||
from core.constants import BRCTL_BIN, ETHTOOL_BIN, IP_BIN, OVS_BIN, TC_BIN
|
||||
from core.utils import check_cmd
|
||||
|
||||
|
||||
def get_net_client(use_ovs, run):
|
||||
"""
|
||||
Retrieve desired net client for running network commands.
|
||||
|
||||
:param bool use_ovs: True for OVS bridges, False for Linux bridges
|
||||
:param func run: function used to run net client commands
|
||||
:return: net client class
|
||||
"""
|
||||
if use_ovs:
|
||||
return OvsNetClient(run)
|
||||
else:
|
||||
return LinuxNetClient(run)
|
||||
|
||||
|
||||
class LinuxNetClient(object):
|
||||
|
@ -28,7 +41,7 @@ class LinuxNetClient(object):
|
|||
:param str name: name for hostname
|
||||
:return: nothing
|
||||
"""
|
||||
self.run(["hostname", name])
|
||||
self.run("hostname %s" % name)
|
||||
|
||||
def create_route(self, route, device):
|
||||
"""
|
||||
|
@ -38,7 +51,7 @@ class LinuxNetClient(object):
|
|||
:param str device: device to add route to
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "route", "add", route, "dev", device])
|
||||
self.run("%s route add %s dev %s" % (IP_BIN, route, device))
|
||||
|
||||
def device_up(self, device):
|
||||
"""
|
||||
|
@ -47,7 +60,7 @@ class LinuxNetClient(object):
|
|||
:param str device: device to bring up
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "link", "set", device, "up"])
|
||||
self.run("%s link set %s up" % (IP_BIN, device))
|
||||
|
||||
def device_down(self, device):
|
||||
"""
|
||||
|
@ -56,7 +69,7 @@ class LinuxNetClient(object):
|
|||
:param str device: device to bring down
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "link", "set", device, "down"])
|
||||
self.run("%s link set %s down" % (IP_BIN, device))
|
||||
|
||||
def device_name(self, device, name):
|
||||
"""
|
||||
|
@ -66,7 +79,7 @@ class LinuxNetClient(object):
|
|||
:param str name: name to set
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "link", "set", device, "name", name])
|
||||
self.run("%s link set %s name %s" % (IP_BIN, device, name))
|
||||
|
||||
def device_show(self, device):
|
||||
"""
|
||||
|
@ -76,7 +89,7 @@ class LinuxNetClient(object):
|
|||
:return: device information
|
||||
:rtype: str
|
||||
"""
|
||||
return self.run([IP_BIN, "link", "show", device])
|
||||
return self.run("%s link show %s" % (IP_BIN, device))
|
||||
|
||||
def device_ns(self, device, namespace):
|
||||
"""
|
||||
|
@ -86,7 +99,7 @@ class LinuxNetClient(object):
|
|||
:param str namespace: namespace to set device to
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "link", "set", device, "netns", namespace])
|
||||
self.run("%s link set %s netns %s" % (IP_BIN, device, namespace))
|
||||
|
||||
def device_flush(self, device):
|
||||
"""
|
||||
|
@ -95,7 +108,7 @@ class LinuxNetClient(object):
|
|||
:param str device: device to flush
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "-6", "address", "flush", "dev", device])
|
||||
self.run("%s -6 address flush dev %s" % (IP_BIN, device))
|
||||
|
||||
def device_mac(self, device, mac):
|
||||
"""
|
||||
|
@ -105,7 +118,7 @@ class LinuxNetClient(object):
|
|||
:param str mac: mac to set
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "link", "set", "dev", device, "address", mac])
|
||||
self.run("%s link set dev %s address %s" % (IP_BIN, device, mac))
|
||||
|
||||
def delete_device(self, device):
|
||||
"""
|
||||
|
@ -114,7 +127,7 @@ class LinuxNetClient(object):
|
|||
:param str device: device to delete
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "link", "delete", device])
|
||||
self.run("%s link delete %s" % (IP_BIN, device))
|
||||
|
||||
def delete_tc(self, device):
|
||||
"""
|
||||
|
@ -123,7 +136,7 @@ class LinuxNetClient(object):
|
|||
:param str device: device to remove tc
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([TC_BIN, "qdisc", "del", "dev", device, "root"])
|
||||
self.run("%s qdisc delete dev %s root" % (TC_BIN, device))
|
||||
|
||||
def checksums_off(self, interface_name):
|
||||
"""
|
||||
|
@ -132,7 +145,7 @@ class LinuxNetClient(object):
|
|||
:param str interface_name: interface to update
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([ETHTOOL_BIN, "-K", interface_name, "rx", "off", "tx", "off"])
|
||||
self.run("%s -K %s rx off tx off" % (ETHTOOL_BIN, interface_name))
|
||||
|
||||
def create_address(self, device, address, broadcast=None):
|
||||
"""
|
||||
|
@ -145,19 +158,11 @@ class LinuxNetClient(object):
|
|||
"""
|
||||
if broadcast is not None:
|
||||
self.run(
|
||||
[
|
||||
IP_BIN,
|
||||
"address",
|
||||
"add",
|
||||
address,
|
||||
"broadcast",
|
||||
broadcast,
|
||||
"dev",
|
||||
device,
|
||||
]
|
||||
"%s address add %s broadcast %s dev %s"
|
||||
% (IP_BIN, address, broadcast, device)
|
||||
)
|
||||
else:
|
||||
self.run([IP_BIN, "address", "add", address, "dev", device])
|
||||
self.run("%s address add %s dev %s" % (IP_BIN, address, device))
|
||||
|
||||
def delete_address(self, device, address):
|
||||
"""
|
||||
|
@ -167,7 +172,7 @@ class LinuxNetClient(object):
|
|||
:param str address: address to remove
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([IP_BIN, "address", "delete", address, "dev", device])
|
||||
self.run("%s address delete %s dev %s" % (IP_BIN, address, device))
|
||||
|
||||
def create_veth(self, name, peer):
|
||||
"""
|
||||
|
@ -177,9 +182,7 @@ class LinuxNetClient(object):
|
|||
:param str peer: peer name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run(
|
||||
[IP_BIN, "link", "add", "name", name, "type", "veth", "peer", "name", peer]
|
||||
)
|
||||
self.run("%s link add name %s type veth peer name %s" % (IP_BIN, name, peer))
|
||||
|
||||
def create_gretap(self, device, address, local, ttl, key):
|
||||
"""
|
||||
|
@ -188,17 +191,17 @@ class LinuxNetClient(object):
|
|||
:param str device: device to add tap to
|
||||
:param str address: address to add tap for
|
||||
:param str local: local address to tie to
|
||||
:param str ttl: time to live value
|
||||
:param str key: key for tap
|
||||
:param int ttl: time to live value
|
||||
:param int key: key for tap
|
||||
:return: nothing
|
||||
"""
|
||||
cmd = [IP_BIN, "link", "add", device, "type", "gretap", "remote", address]
|
||||
cmd = "%s link add %s type gretap remote %s" % (IP_BIN, device, address)
|
||||
if local is not None:
|
||||
cmd.extend(["local", local])
|
||||
cmd += " local %s" % local
|
||||
if ttl is not None:
|
||||
cmd.extend(["ttl", ttl])
|
||||
cmd += " ttl %s" % ttl
|
||||
if key is not None:
|
||||
cmd.extend(["key", key])
|
||||
cmd += " key %s" % key
|
||||
self.run(cmd)
|
||||
|
||||
def create_bridge(self, name):
|
||||
|
@ -208,9 +211,9 @@ class LinuxNetClient(object):
|
|||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([BRCTL_BIN, "addbr", name])
|
||||
self.run([BRCTL_BIN, "stp", name, "off"])
|
||||
self.run([BRCTL_BIN, "setfd", name, "0"])
|
||||
self.run("%s addbr %s" % (BRCTL_BIN, name))
|
||||
self.run("%s stp %s off" % (BRCTL_BIN, name))
|
||||
self.run("%s setfd %s 0" % (BRCTL_BIN, name))
|
||||
self.device_up(name)
|
||||
|
||||
# turn off multicast snooping so forwarding occurs w/o IGMP joins
|
||||
|
@ -227,7 +230,7 @@ class LinuxNetClient(object):
|
|||
:return: nothing
|
||||
"""
|
||||
self.device_down(name)
|
||||
self.run([BRCTL_BIN, "delbr", name])
|
||||
self.run("%s delbr %s" % (BRCTL_BIN, name))
|
||||
|
||||
def create_interface(self, bridge_name, interface_name):
|
||||
"""
|
||||
|
@ -237,7 +240,7 @@ class LinuxNetClient(object):
|
|||
:param str interface_name: interface name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([BRCTL_BIN, "addif", bridge_name, interface_name])
|
||||
self.run("%s addif %s %s" % (BRCTL_BIN, bridge_name, interface_name))
|
||||
self.device_up(interface_name)
|
||||
|
||||
def delete_interface(self, bridge_name, interface_name):
|
||||
|
@ -248,7 +251,7 @@ class LinuxNetClient(object):
|
|||
:param str interface_name: interface name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([BRCTL_BIN, "delif", bridge_name, interface_name])
|
||||
self.run("%s delif %s %s" % (BRCTL_BIN, bridge_name, interface_name))
|
||||
|
||||
def existing_bridges(self, _id):
|
||||
"""
|
||||
|
@ -256,7 +259,7 @@ class LinuxNetClient(object):
|
|||
|
||||
:param _id: node id to check bridges for
|
||||
"""
|
||||
output = self.run([BRCTL_BIN, "show"])
|
||||
output = self.run("%s show" % BRCTL_BIN)
|
||||
lines = output.split("\n")
|
||||
for line in lines[1:]:
|
||||
columns = line.split()
|
||||
|
@ -275,7 +278,7 @@ class LinuxNetClient(object):
|
|||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
check_cmd([BRCTL_BIN, "setageing", name, "0"])
|
||||
self.run("%s setageing %s 0" % (BRCTL_BIN, name))
|
||||
|
||||
|
||||
class OvsNetClient(LinuxNetClient):
|
||||
|
@ -290,10 +293,10 @@ class OvsNetClient(LinuxNetClient):
|
|||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([OVS_BIN, "add-br", name])
|
||||
self.run([OVS_BIN, "set", "bridge", name, "stp_enable=false"])
|
||||
self.run([OVS_BIN, "set", "bridge", name, "other_config:stp-max-age=6"])
|
||||
self.run([OVS_BIN, "set", "bridge", name, "other_config:stp-forward-delay=4"])
|
||||
self.run("%s add-br %s" % (OVS_BIN, name))
|
||||
self.run("%s set bridge %s stp_enable=false" % (OVS_BIN, name))
|
||||
self.run("%s set bridge %s other_config:stp-max-age=6" % (OVS_BIN, name))
|
||||
self.run("%s set bridge %s other_config:stp-forward-delay=4" % (OVS_BIN, name))
|
||||
self.device_up(name)
|
||||
|
||||
def delete_bridge(self, name):
|
||||
|
@ -304,7 +307,7 @@ class OvsNetClient(LinuxNetClient):
|
|||
:return: nothing
|
||||
"""
|
||||
self.device_down(name)
|
||||
self.run([OVS_BIN, "del-br", name])
|
||||
self.run("%s del-br %s" % (OVS_BIN, name))
|
||||
|
||||
def create_interface(self, bridge_name, interface_name):
|
||||
"""
|
||||
|
@ -314,7 +317,7 @@ class OvsNetClient(LinuxNetClient):
|
|||
:param str interface_name: interface name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([OVS_BIN, "add-port", bridge_name, interface_name])
|
||||
self.run("%s add-port %s %s" % (OVS_BIN, bridge_name, interface_name))
|
||||
self.device_up(interface_name)
|
||||
|
||||
def delete_interface(self, bridge_name, interface_name):
|
||||
|
@ -325,7 +328,7 @@ class OvsNetClient(LinuxNetClient):
|
|||
:param str interface_name: interface name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([OVS_BIN, "del-port", bridge_name, interface_name])
|
||||
self.run("%s del-port %s %s" % (OVS_BIN, bridge_name, interface_name))
|
||||
|
||||
def existing_bridges(self, _id):
|
||||
"""
|
||||
|
@ -333,7 +336,7 @@ class OvsNetClient(LinuxNetClient):
|
|||
|
||||
:param _id: node id to check bridges for
|
||||
"""
|
||||
output = self.run([OVS_BIN, "list-br"])
|
||||
output = self.run("%s list-br" % OVS_BIN)
|
||||
if output:
|
||||
for line in output.split("\n"):
|
||||
fields = line.split(".")
|
||||
|
@ -348,4 +351,4 @@ class OvsNetClient(LinuxNetClient):
|
|||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
self.run([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"])
|
||||
self.run("%s set bridge %s other_config:mac-aging-time=0" % (OVS_BIN, name))
|
||||
|
|
|
@ -3,19 +3,20 @@ Defines network nodes used within core.
|
|||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from core import constants, utils
|
||||
from core import utils
|
||||
from core.constants import EBTABLES_BIN, TC_BIN
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.interface import GreTap, Veth
|
||||
from core.nodes.netclient import get_net_client
|
||||
|
||||
ebtables_lock = threading.Lock()
|
||||
|
||||
|
@ -92,14 +93,11 @@ class EbtablesQueue(object):
|
|||
"""
|
||||
Helper for building ebtables atomic file command list.
|
||||
|
||||
:param list[str] cmd: ebtable command
|
||||
:param str cmd: ebtable command
|
||||
:return: ebtable atomic command
|
||||
:rtype: list[str]
|
||||
"""
|
||||
r = [constants.EBTABLES_BIN, "--atomic-file", self.atomic_file]
|
||||
if cmd:
|
||||
r.extend(cmd)
|
||||
return r
|
||||
return "%s --atomic-file %s %s" % (EBTABLES_BIN, self.atomic_file, cmd)
|
||||
|
||||
def lastupdate(self, wlan):
|
||||
"""
|
||||
|
@ -163,22 +161,22 @@ class EbtablesQueue(object):
|
|||
:return: nothing
|
||||
"""
|
||||
# save kernel ebtables snapshot to a file
|
||||
args = self.ebatomiccmd(["--atomic-save"])
|
||||
utils.check_cmd(args)
|
||||
args = self.ebatomiccmd("--atomic-save")
|
||||
wlan.net_cmd(args)
|
||||
|
||||
# modify the table file using queued ebtables commands
|
||||
for c in self.cmds:
|
||||
args = self.ebatomiccmd(c)
|
||||
utils.check_cmd(args)
|
||||
wlan.net_cmd(args)
|
||||
self.cmds = []
|
||||
|
||||
# commit the table file to the kernel
|
||||
args = self.ebatomiccmd(["--atomic-commit"])
|
||||
utils.check_cmd(args)
|
||||
args = self.ebatomiccmd("--atomic-commit")
|
||||
wlan.net_cmd(args)
|
||||
|
||||
try:
|
||||
os.unlink(self.atomic_file)
|
||||
except OSError:
|
||||
wlan.net_cmd("rm -f %s" % self.atomic_file)
|
||||
except CoreCommandError:
|
||||
logging.exception("error removing atomic file: %s", self.atomic_file)
|
||||
|
||||
def ebchange(self, wlan):
|
||||
|
@ -200,58 +198,26 @@ class EbtablesQueue(object):
|
|||
"""
|
||||
with wlan._linked_lock:
|
||||
# flush the chain
|
||||
self.cmds.extend([["-F", wlan.brname]])
|
||||
self.cmds.append("-F %s" % wlan.brname)
|
||||
# rebuild the chain
|
||||
for netif1, v in wlan._linked.items():
|
||||
for netif2, linked in v.items():
|
||||
if wlan.policy == "DROP" and linked:
|
||||
self.cmds.extend(
|
||||
[
|
||||
[
|
||||
"-A",
|
||||
wlan.brname,
|
||||
"-i",
|
||||
netif1.localname,
|
||||
"-o",
|
||||
netif2.localname,
|
||||
"-j",
|
||||
"ACCEPT",
|
||||
],
|
||||
[
|
||||
"-A",
|
||||
wlan.brname,
|
||||
"-o",
|
||||
netif1.localname,
|
||||
"-i",
|
||||
netif2.localname,
|
||||
"-j",
|
||||
"ACCEPT",
|
||||
],
|
||||
"-A %s -i %s -o %s -j ACCEPT"
|
||||
% (wlan.brname, netif1.localname, netif2.localname),
|
||||
"-A %s -o %s -i %s -j ACCEPT"
|
||||
% (wlan.brname, netif1.localname, netif2.localname),
|
||||
]
|
||||
)
|
||||
elif wlan.policy == "ACCEPT" and not linked:
|
||||
self.cmds.extend(
|
||||
[
|
||||
[
|
||||
"-A",
|
||||
wlan.brname,
|
||||
"-i",
|
||||
netif1.localname,
|
||||
"-o",
|
||||
netif2.localname,
|
||||
"-j",
|
||||
"DROP",
|
||||
],
|
||||
[
|
||||
"-A",
|
||||
wlan.brname,
|
||||
"-o",
|
||||
netif1.localname,
|
||||
"-i",
|
||||
netif2.localname,
|
||||
"-j",
|
||||
"DROP",
|
||||
],
|
||||
"-A %s -i %s -o %s -j DROP"
|
||||
% (wlan.brname, netif1.localname, netif2.localname),
|
||||
"-A %s -o %s -i %s -j DROP"
|
||||
% (wlan.brname, netif1.localname, netif2.localname),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -281,7 +247,9 @@ class CoreNetwork(CoreNetworkBase):
|
|||
|
||||
policy = "DROP"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||
def __init__(
|
||||
self, session, _id=None, name=None, start=True, server=None, policy=None
|
||||
):
|
||||
"""
|
||||
Creates a LxBrNet instance.
|
||||
|
||||
|
@ -289,9 +257,11 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param policy: network policy
|
||||
"""
|
||||
CoreNetworkBase.__init__(self, session, _id, name, start)
|
||||
CoreNetworkBase.__init__(self, session, _id, name, start, server)
|
||||
if name is None:
|
||||
name = str(self.id)
|
||||
if policy is not None:
|
||||
|
@ -304,6 +274,24 @@ class CoreNetwork(CoreNetworkBase):
|
|||
self.startup()
|
||||
ebq.startupdateloop(self)
|
||||
|
||||
def net_cmd(self, args, env=None, cwd=None, wait=True):
|
||||
"""
|
||||
Runs a command that is used to configure and setup the network on the host
|
||||
system and all configured distributed servers.
|
||||
|
||||
:param str args: command to run
|
||||
:param dict env: environment to run command with
|
||||
:param str cwd: directory to run command in
|
||||
:param bool wait: True to wait for status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
logging.info("network node(%s) cmd", self.name)
|
||||
output = utils.check_cmd(args, env, cwd, wait)
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(args, env, cwd, wait))
|
||||
return output
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
Linux bridge starup logic.
|
||||
|
@ -314,21 +302,12 @@ class CoreNetwork(CoreNetworkBase):
|
|||
self.net_client.create_bridge(self.brname)
|
||||
|
||||
# create a new ebtables chain for this bridge
|
||||
ebtablescmds(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-A",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.brname,
|
||||
"-j",
|
||||
self.brname,
|
||||
],
|
||||
],
|
||||
)
|
||||
cmds = [
|
||||
"%s -N %s -P %s" % (EBTABLES_BIN, self.brname, self.policy),
|
||||
"%s -A FORWARD --logical-in %s -j %s"
|
||||
% (EBTABLES_BIN, self.brname, self.brname),
|
||||
]
|
||||
ebtablescmds(self.net_cmd, cmds)
|
||||
|
||||
self.up = True
|
||||
|
||||
|
@ -345,21 +324,12 @@ class CoreNetwork(CoreNetworkBase):
|
|||
|
||||
try:
|
||||
self.net_client.delete_bridge(self.brname)
|
||||
ebtablescmds(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-D",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.brname,
|
||||
"-j",
|
||||
self.brname,
|
||||
],
|
||||
[constants.EBTABLES_BIN, "-X", self.brname],
|
||||
],
|
||||
)
|
||||
cmds = [
|
||||
"%s -D FORWARD --logical-in %s -j %s"
|
||||
% (EBTABLES_BIN, self.brname, self.brname),
|
||||
"%s -X %s" % (EBTABLES_BIN, self.brname),
|
||||
]
|
||||
ebtablescmds(self.net_cmd, cmds)
|
||||
except CoreCommandError:
|
||||
logging.exception("error during shutdown")
|
||||
|
||||
|
@ -378,11 +348,11 @@ class CoreNetwork(CoreNetworkBase):
|
|||
"""
|
||||
Attach a network interface.
|
||||
|
||||
:param core.netns.vnode.VEth netif: network interface to attach
|
||||
:param core.nodes.interface.Veth netif: network interface to attach
|
||||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
self.net_client.create_interface(self.brname, netif.localname)
|
||||
netif.net_client.create_interface(self.brname, netif.localname)
|
||||
|
||||
CoreNetworkBase.attach(self, netif)
|
||||
|
||||
|
@ -394,7 +364,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
self.net_client.delete_interface(self.brname, netif.localname)
|
||||
netif.net_client.delete_interface(self.brname, netif.localname)
|
||||
|
||||
CoreNetworkBase.detach(self, netif)
|
||||
|
||||
|
@ -485,8 +455,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
"""
|
||||
if devname is None:
|
||||
devname = netif.localname
|
||||
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
|
||||
parent = ["root"]
|
||||
tc = "%s qdisc replace dev %s" % (TC_BIN, devname)
|
||||
parent = "root"
|
||||
changed = False
|
||||
if netif.setparam("bw", bw):
|
||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
||||
|
@ -494,27 +464,24 @@ class CoreNetwork(CoreNetworkBase):
|
|||
burst = max(2 * netif.mtu, bw / 1000)
|
||||
# max IP payload
|
||||
limit = 0xFFFF
|
||||
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
||||
tbf = "tbf rate %s burst %s limit %s" % (bw, burst, limit)
|
||||
if bw > 0:
|
||||
if self.up:
|
||||
logging.debug(
|
||||
"linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],)
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||
cmd = "%s %s handle 1: %s" % (tc, parent, tbf)
|
||||
netif.net_cmd(cmd)
|
||||
netif.setparam("has_tbf", True)
|
||||
changed = True
|
||||
elif netif.getparam("has_tbf") and bw <= 0:
|
||||
tcd = [] + tc
|
||||
tcd[2] = "delete"
|
||||
if self.up:
|
||||
utils.check_cmd(tcd + parent)
|
||||
cmd = "%s qdisc delete dev %s %s" % (TC_BIN, devname, parent)
|
||||
netif.net_cmd(cmd)
|
||||
netif.setparam("has_tbf", False)
|
||||
# removing the parent removes the child
|
||||
netif.setparam("has_netem", False)
|
||||
changed = True
|
||||
if netif.getparam("has_tbf"):
|
||||
parent = ["parent", "1:1"]
|
||||
netem = ["netem"]
|
||||
parent = "parent 1:1"
|
||||
netem = "netem"
|
||||
changed = max(changed, netif.setparam("delay", delay))
|
||||
if loss is not None:
|
||||
loss = float(loss)
|
||||
|
@ -527,17 +494,17 @@ class CoreNetwork(CoreNetworkBase):
|
|||
return
|
||||
# jitter and delay use the same delay statement
|
||||
if delay is not None:
|
||||
netem += ["delay", "%sus" % delay]
|
||||
netem += " delay %sus" % delay
|
||||
if jitter is not None:
|
||||
if delay is None:
|
||||
netem += ["delay", "0us", "%sus" % jitter, "25%"]
|
||||
netem += " delay 0us %sus 25%%" % jitter
|
||||
else:
|
||||
netem += ["%sus" % jitter, "25%"]
|
||||
netem += " %sus 25%%" % jitter
|
||||
|
||||
if loss is not None and loss > 0:
|
||||
netem += ["loss", "%s%%" % min(loss, 100)]
|
||||
netem += " loss %s%%" % min(loss, 100)
|
||||
if duplicate is not None and duplicate > 0:
|
||||
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
||||
netem += " duplicate %s%%" % min(duplicate, 100)
|
||||
|
||||
delay_check = delay is None or delay <= 0
|
||||
jitter_check = jitter is None or jitter <= 0
|
||||
|
@ -547,17 +514,19 @@ class CoreNetwork(CoreNetworkBase):
|
|||
# possibly remove netem if it exists and parent queue wasn't removed
|
||||
if not netif.getparam("has_netem"):
|
||||
return
|
||||
tc[2] = "delete"
|
||||
if self.up:
|
||||
logging.debug("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
||||
utils.check_cmd(tc + parent + ["handle", "10:"])
|
||||
cmd = "%s qdisc delete dev %s %s handle 10:" % (TC_BIN, devname, parent)
|
||||
netif.net_cmd(cmd)
|
||||
netif.setparam("has_netem", False)
|
||||
elif len(netem) > 1:
|
||||
if self.up:
|
||||
logging.debug(
|
||||
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
||||
cmd = "%s qdisc replace dev %s %s handle 10: %s" % (
|
||||
TC_BIN,
|
||||
devname,
|
||||
parent,
|
||||
netem,
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||
netif.net_cmd(cmd)
|
||||
netif.setparam("has_netem", True)
|
||||
|
||||
def linknet(self, net):
|
||||
|
@ -588,13 +557,11 @@ class CoreNetwork(CoreNetworkBase):
|
|||
if len(name) >= 16:
|
||||
raise ValueError("interface name %s too long" % name)
|
||||
|
||||
netif = Veth(
|
||||
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
||||
)
|
||||
netif = Veth(self.session, None, name, localname, start=self.up)
|
||||
self.attach(netif)
|
||||
if net.up:
|
||||
# this is similar to net.attach() but uses netif.name instead of localname
|
||||
self.net_client.create_interface(net.brname, netif.name)
|
||||
netif.net_client.create_interface(net.brname, netif.name)
|
||||
i = net.newifindex()
|
||||
net._netif[i] = netif
|
||||
with net._linked_lock:
|
||||
|
@ -649,6 +616,7 @@ class GreTapBridge(CoreNetwork):
|
|||
ttl=255,
|
||||
key=None,
|
||||
start=True,
|
||||
server=None,
|
||||
):
|
||||
"""
|
||||
Create a GreTapBridge instance.
|
||||
|
@ -662,10 +630,10 @@ class GreTapBridge(CoreNetwork):
|
|||
:param ttl: ttl value
|
||||
:param key: gre tap key
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
CoreNetwork.__init__(
|
||||
self, session=session, _id=_id, name=name, policy=policy, start=False
|
||||
)
|
||||
CoreNetwork.__init__(self, session, _id, name, False, server, policy)
|
||||
self.grekey = key
|
||||
if self.grekey is None:
|
||||
self.grekey = self.session.id ^ self.id
|
||||
|
@ -769,6 +737,7 @@ class CtrlNet(CoreNetwork):
|
|||
prefix=None,
|
||||
hostid=None,
|
||||
start=True,
|
||||
server=None,
|
||||
assign_address=True,
|
||||
updown_script=None,
|
||||
serverintf=None,
|
||||
|
@ -782,6 +751,8 @@ class CtrlNet(CoreNetwork):
|
|||
:param prefix: control network ipv4 prefix
|
||||
:param hostid: host id
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param str assign_address: assigned address
|
||||
:param str updown_script: updown script
|
||||
:param serverintf: server interface
|
||||
|
@ -792,7 +763,26 @@ class CtrlNet(CoreNetwork):
|
|||
self.assign_address = assign_address
|
||||
self.updown_script = updown_script
|
||||
self.serverintf = serverintf
|
||||
CoreNetwork.__init__(self, session, _id=_id, name=name, start=start)
|
||||
CoreNetwork.__init__(self, session, _id, name, start, server)
|
||||
|
||||
def add_addresses(self, address):
|
||||
"""
|
||||
Add addresses used for created control networks,
|
||||
|
||||
:param core.nodes.interfaces.IpAddress address: starting address to use
|
||||
:return:
|
||||
"""
|
||||
use_ovs = self.session.options.get_config("ovs") == "True"
|
||||
current = "%s/%s" % (address, self.prefix.prefixlen)
|
||||
net_client = get_net_client(use_ovs, utils.check_cmd)
|
||||
net_client.create_address(self.brname, current)
|
||||
servers = self.session.distributed.servers
|
||||
for name in servers:
|
||||
server = servers[name]
|
||||
address -= 1
|
||||
current = "%s/%s" % (address, self.prefix.prefixlen)
|
||||
net_client = get_net_client(use_ovs, server.remote_cmd)
|
||||
net_client.create_address(self.brname, current)
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
|
@ -806,17 +796,14 @@ class CtrlNet(CoreNetwork):
|
|||
|
||||
CoreNetwork.startup(self)
|
||||
|
||||
if self.hostid:
|
||||
addr = self.prefix.addr(self.hostid)
|
||||
else:
|
||||
addr = self.prefix.max_addr()
|
||||
|
||||
logging.info("added control network bridge: %s %s", self.brname, self.prefix)
|
||||
|
||||
if self.assign_address:
|
||||
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
||||
self.addrconfig(addrlist=addrlist)
|
||||
logging.info("address %s", addr)
|
||||
if self.hostid and self.assign_address:
|
||||
address = self.prefix.addr(self.hostid)
|
||||
self.add_addresses(address)
|
||||
elif self.assign_address:
|
||||
address = self.prefix.max_addr()
|
||||
self.add_addresses(address)
|
||||
|
||||
if self.updown_script:
|
||||
logging.info(
|
||||
|
@ -824,7 +811,7 @@ class CtrlNet(CoreNetwork):
|
|||
self.brname,
|
||||
self.updown_script,
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
||||
self.net_cmd("%s %s startup" % (self.updown_script, self.brname))
|
||||
|
||||
if self.serverintf:
|
||||
self.net_client.create_interface(self.brname, self.serverintf)
|
||||
|
@ -852,7 +839,7 @@ class CtrlNet(CoreNetwork):
|
|||
self.brname,
|
||||
self.updown_script,
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.brname, "shutdown"])
|
||||
self.net_cmd("%s %s shutdown" % (self.updown_script, self.brname))
|
||||
except CoreCommandError:
|
||||
logging.exception("error issuing shutdown script shutdown")
|
||||
|
||||
|
@ -1028,7 +1015,7 @@ class HubNode(CoreNetwork):
|
|||
policy = "ACCEPT"
|
||||
type = "hub"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True):
|
||||
def __init__(self, session, _id=None, name=None, start=True, server=None):
|
||||
"""
|
||||
Creates a HubNode instance.
|
||||
|
||||
|
@ -1036,9 +1023,11 @@ class HubNode(CoreNetwork):
|
|||
:param int _id: node id
|
||||
:param str name: node namee
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
CoreNetwork.__init__(self, session, _id, name, start)
|
||||
CoreNetwork.__init__(self, session, _id, name, start, server)
|
||||
|
||||
# TODO: move to startup method
|
||||
if start:
|
||||
|
@ -1055,7 +1044,9 @@ class WlanNode(CoreNetwork):
|
|||
policy = "DROP"
|
||||
type = "wlan"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||
def __init__(
|
||||
self, session, _id=None, name=None, start=True, server=None, policy=None
|
||||
):
|
||||
"""
|
||||
Create a WlanNode instance.
|
||||
|
||||
|
@ -1063,9 +1054,11 @@ class WlanNode(CoreNetwork):
|
|||
:param int _id: node id
|
||||
:param str name: node name
|
||||
:param bool start: start flag
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param policy: wlan policy
|
||||
"""
|
||||
CoreNetwork.__init__(self, session, _id, name, start, policy)
|
||||
CoreNetwork.__init__(self, session, _id, name, start, server, policy)
|
||||
# wireless model such as basic range
|
||||
self.model = None
|
||||
# mobility model such as scripted
|
||||
|
|
|
@ -4,20 +4,24 @@ PhysicalNode class for including real systems in the emulated network.
|
|||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from core import constants, utils
|
||||
from core import utils
|
||||
from core.constants import MOUNT_BIN, UMOUNT_BIN
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.errors import CoreCommandError
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.network import CoreNetwork, GreTap
|
||||
|
||||
|
||||
class PhysicalNode(CoreNodeBase):
|
||||
def __init__(self, session, _id=None, name=None, nodedir=None, start=True):
|
||||
CoreNodeBase.__init__(self, session, _id, name, start=start)
|
||||
def __init__(
|
||||
self, session, _id=None, name=None, nodedir=None, start=True, server=None
|
||||
):
|
||||
CoreNodeBase.__init__(self, session, _id, name, start, server)
|
||||
if not self.server:
|
||||
raise CoreError("physical nodes must be assigned to a remote server")
|
||||
self.nodedir = nodedir
|
||||
self.up = start
|
||||
self.lock = threading.RLock()
|
||||
|
@ -52,50 +56,6 @@ class PhysicalNode(CoreNodeBase):
|
|||
"""
|
||||
return sh
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs shell command on node, with option to not wait for a result.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param bool wait: wait for command to exit, defaults to True
|
||||
:return: exit status for command
|
||||
:rtype: int
|
||||
"""
|
||||
os.chdir(self.nodedir)
|
||||
status = utils.cmd(args, wait)
|
||||
return status
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Runs shell command on node and get exit status and output.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exit status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
os.chdir(self.nodedir)
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
stdout, _ = p.communicate()
|
||||
status = p.wait()
|
||||
return status, stdout.strip()
|
||||
|
||||
def check_cmd(self, args):
|
||||
"""
|
||||
Runs shell command on node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
status, output = self.cmd_output(args)
|
||||
if status:
|
||||
raise CoreCommandError(status, args, output)
|
||||
return output.strip()
|
||||
|
||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||
return self.cmd([sh, "-c", cmdstr])
|
||||
|
||||
def sethwaddr(self, ifindex, addr):
|
||||
"""
|
||||
Set hardware address for an interface.
|
||||
|
@ -130,7 +90,6 @@ class PhysicalNode(CoreNodeBase):
|
|||
|
||||
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
||||
"""
|
||||
The broker builds a GreTap tunnel device to this physical node.
|
||||
When a link message is received linking this node to another part of
|
||||
the emulation, no new interface is created; instead, adopt the
|
||||
GreTap netif as the node interface.
|
||||
|
@ -201,23 +160,17 @@ class PhysicalNode(CoreNodeBase):
|
|||
if ifindex is None:
|
||||
ifindex = self.newifindex()
|
||||
|
||||
if self.up:
|
||||
# this is reached when this node is linked to a network node
|
||||
# tunnel to net not built yet, so build it now and adopt it
|
||||
gt = self.session.broker.addnettunnel(net.id)
|
||||
if gt is None or len(gt) != 1:
|
||||
raise ValueError(
|
||||
"error building tunnel from adding a new network interface: %s" % gt
|
||||
)
|
||||
gt = gt[0]
|
||||
net.detach(gt)
|
||||
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
|
||||
return ifindex
|
||||
|
||||
# this is reached when configuring services (self.up=False)
|
||||
if ifname is None:
|
||||
ifname = "gt%d" % ifindex
|
||||
|
||||
if self.up:
|
||||
# this is reached when this node is linked to a network node
|
||||
# tunnel to net not built yet, so build it now and adopt it
|
||||
_, remote_tap = self.session.distributed.create_gre_tunnel(net, self.server)
|
||||
self.adoptnetif(remote_tap, ifindex, hwaddr, addrlist)
|
||||
return ifindex
|
||||
else:
|
||||
# this is reached when configuring services (self.up=False)
|
||||
netif = GreTap(node=self, name=ifname, session=self.session, start=False)
|
||||
self.adoptnetif(netif, ifindex, hwaddr, addrlist)
|
||||
return ifindex
|
||||
|
@ -235,13 +188,13 @@ class PhysicalNode(CoreNodeBase):
|
|||
source = os.path.abspath(source)
|
||||
logging.info("mounting %s at %s", source, target)
|
||||
os.makedirs(target)
|
||||
self.check_cmd([constants.MOUNT_BIN, "--bind", source, target])
|
||||
self.net_cmd("%s --bind %s %s" % (MOUNT_BIN, source, target), cwd=self.nodedir)
|
||||
self._mounts.append((source, target))
|
||||
|
||||
def umount(self, target):
|
||||
logging.info("unmounting '%s'" % target)
|
||||
try:
|
||||
self.check_cmd([constants.UMOUNT_BIN, "-l", target])
|
||||
self.net_cmd("%s -l %s" % (UMOUNT_BIN, target), cwd=self.nodedir)
|
||||
except CoreCommandError:
|
||||
logging.exception("unmounting failed for %s", target)
|
||||
|
||||
|
@ -267,6 +220,9 @@ class PhysicalNode(CoreNodeBase):
|
|||
os.chmod(node_file.name, mode)
|
||||
logging.info("created nodefile: '%s'; mode: 0%o", node_file.name, mode)
|
||||
|
||||
def node_net_cmd(self, args, wait=True):
|
||||
return self.net_cmd(args, wait=wait)
|
||||
|
||||
|
||||
class Rj45Node(CoreNodeBase, CoreInterface):
|
||||
"""
|
||||
|
@ -277,7 +233,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
apitype = NodeTypes.RJ45.value
|
||||
type = "rj45"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, mtu=1500, start=True):
|
||||
def __init__(self, session, _id=None, name=None, mtu=1500, start=True, server=None):
|
||||
"""
|
||||
Create an RJ45Node instance.
|
||||
|
||||
|
@ -286,10 +242,11 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
:param str name: node name
|
||||
:param mtu: rj45 mtu
|
||||
:param bool start: start flag
|
||||
:return:
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
"""
|
||||
CoreNodeBase.__init__(self, session, _id, name, start=start)
|
||||
CoreInterface.__init__(self, node=self, name=name, mtu=mtu)
|
||||
CoreNodeBase.__init__(self, session, _id, name, start, server)
|
||||
CoreInterface.__init__(self, session, self, name, mtu, server)
|
||||
self.up = False
|
||||
self.lock = threading.RLock()
|
||||
self.ifindex = None
|
||||
|
@ -528,38 +485,6 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
CoreInterface.setposition(self, x, y, z)
|
||||
return result
|
||||
|
||||
def check_cmd(self, args):
|
||||
"""
|
||||
Runs shell command on node.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exist status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def cmd(self, args, wait=True):
|
||||
"""
|
||||
Runs shell command on node, with option to not wait for a result.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:param bool wait: wait for command to exit, defaults to True
|
||||
:return: exit status for command
|
||||
:rtype: int
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def cmd_output(self, args):
|
||||
"""
|
||||
Runs shell command on node and get exit status and output.
|
||||
|
||||
:param list[str]|str args: command to run
|
||||
:return: exit status and combined stdout and stderr
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def termcmdstring(self, sh):
|
||||
"""
|
||||
Create a terminal command string.
|
||||
|
|
|
@ -76,7 +76,6 @@ class Sdt(object):
|
|||
# node information for remote nodes not in session._objs
|
||||
# local nodes also appear here since their obj may not exist yet
|
||||
self.remotes = {}
|
||||
session.broker.handlers.add(self.handle_distributed)
|
||||
|
||||
# add handler for node updates
|
||||
self.session.node_handlers.append(self.handle_node_update)
|
||||
|
|
|
@ -598,7 +598,7 @@ class CoreServices(object):
|
|||
for cmd in cmds:
|
||||
logging.debug("validating service(%s) using: %s", service.name, cmd)
|
||||
try:
|
||||
node.check_cmd(cmd)
|
||||
node.node_net_cmd(cmd)
|
||||
except CoreCommandError as e:
|
||||
logging.debug(
|
||||
"node(%s) service(%s) validate failed", node.name, service.name
|
||||
|
@ -631,7 +631,7 @@ class CoreServices(object):
|
|||
status = 0
|
||||
for args in service.shutdown:
|
||||
try:
|
||||
node.check_cmd(args)
|
||||
node.node_net_cmd(args)
|
||||
except CoreCommandError:
|
||||
logging.exception("error running stop command %s", args)
|
||||
status = -1
|
||||
|
@ -729,10 +729,7 @@ class CoreServices(object):
|
|||
status = 0
|
||||
for cmd in cmds:
|
||||
try:
|
||||
if wait:
|
||||
node.check_cmd(cmd)
|
||||
else:
|
||||
node.cmd(cmd, wait=False)
|
||||
node.node_net_cmd(cmd, wait)
|
||||
except CoreCommandError:
|
||||
logging.exception("error starting command")
|
||||
status = -1
|
||||
|
|
|
@ -415,9 +415,11 @@ class HttpService(UtilService):
|
|||
Detect the apache2 version using the 'a2query' command.
|
||||
"""
|
||||
try:
|
||||
status, result = utils.cmd_output(["a2query", "-v"])
|
||||
except CoreCommandError:
|
||||
status = -1
|
||||
result = utils.check_cmd("a2query -v")
|
||||
status = 0
|
||||
except CoreCommandError as e:
|
||||
status = e.returncode
|
||||
result = e.stderr
|
||||
|
||||
if status == 0 and result[:3] == "2.4":
|
||||
return cls.APACHEVER24
|
||||
|
|
|
@ -11,10 +11,8 @@ import logging
|
|||
import logging.config
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from past.builtins import basestring
|
||||
from subprocess import PIPE, STDOUT, Popen
|
||||
|
||||
from core.errors import CoreCommandError
|
||||
|
||||
|
@ -177,20 +175,6 @@ def make_tuple_fromstr(s, value_type):
|
|||
return tuple(value_type(i) for i in values)
|
||||
|
||||
|
||||
def split_args(args):
|
||||
"""
|
||||
Convenience method for splitting potential string commands into a shell-like
|
||||
syntax list.
|
||||
|
||||
:param list/str args: command list or string
|
||||
:return: shell-like syntax list
|
||||
:rtype: list
|
||||
"""
|
||||
if isinstance(args, basestring):
|
||||
args = shlex.split(args)
|
||||
return args
|
||||
|
||||
|
||||
def mute_detach(args, **kwargs):
|
||||
"""
|
||||
Run a muted detached process by forking it.
|
||||
|
@ -200,77 +184,39 @@ def mute_detach(args, **kwargs):
|
|||
:return: process id of the command
|
||||
:rtype: int
|
||||
"""
|
||||
args = split_args(args)
|
||||
args = shlex.split(args)
|
||||
kwargs["preexec_fn"] = _detach_init
|
||||
kwargs["stdout"] = DEVNULL
|
||||
kwargs["stderr"] = subprocess.STDOUT
|
||||
return subprocess.Popen(args, **kwargs).pid
|
||||
kwargs["stderr"] = STDOUT
|
||||
return Popen(args, **kwargs).pid
|
||||
|
||||
|
||||
def cmd(args, wait=True):
|
||||
"""
|
||||
Runs a command on and returns the exit status.
|
||||
|
||||
:param list[str]|str args: command arguments
|
||||
:param bool wait: wait for command to end or not
|
||||
:return: command status
|
||||
:rtype: int
|
||||
"""
|
||||
args = split_args(args)
|
||||
logging.debug("command: %s", args)
|
||||
try:
|
||||
p = subprocess.Popen(args)
|
||||
if not wait:
|
||||
return 0
|
||||
return p.wait()
|
||||
except OSError:
|
||||
raise CoreCommandError(-1, args)
|
||||
|
||||
|
||||
def cmd_output(args):
|
||||
def check_cmd(args, env=None, cwd=None, wait=True):
|
||||
"""
|
||||
Execute a command on the host and return a tuple containing the exit status and
|
||||
result string. stderr output is folded into the stdout result string.
|
||||
|
||||
:param list[str]|str args: command arguments
|
||||
:return: command status and stdout
|
||||
:rtype: tuple[int, str]
|
||||
:raises CoreCommandError: when the file to execute is not found
|
||||
"""
|
||||
args = split_args(args)
|
||||
logging.debug("command: %s", args)
|
||||
try:
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
stdout, _ = p.communicate()
|
||||
status = p.wait()
|
||||
return status, stdout.decode("utf-8").strip()
|
||||
except OSError:
|
||||
raise CoreCommandError(-1, args)
|
||||
|
||||
|
||||
def check_cmd(args, **kwargs):
|
||||
"""
|
||||
Execute a command on the host and return a tuple containing the exit status and
|
||||
result string. stderr output is folded into the stdout result string.
|
||||
|
||||
:param list[str]|str args: command arguments
|
||||
:param dict kwargs: keyword arguments to pass to subprocess.Popen
|
||||
:param str args: command arguments
|
||||
:param dict env: environment to run command with
|
||||
:param str cwd: directory to run command in
|
||||
:param bool wait: True to wait for status, False otherwise
|
||||
:return: combined stdout and stderr
|
||||
:rtype: str
|
||||
:raises CoreCommandError: when there is a non-zero exit status or the file to
|
||||
execute is not found
|
||||
"""
|
||||
kwargs["stdout"] = subprocess.PIPE
|
||||
kwargs["stderr"] = subprocess.STDOUT
|
||||
args = split_args(args)
|
||||
logging.debug("command: %s", args)
|
||||
logging.info("command cwd(%s) wait(%s): %s", cwd, wait, args)
|
||||
args = shlex.split(args)
|
||||
try:
|
||||
p = subprocess.Popen(args, **kwargs)
|
||||
stdout, _ = p.communicate()
|
||||
p = Popen(args, stdout=PIPE, stderr=PIPE, env=env, cwd=cwd)
|
||||
if wait:
|
||||
stdout, stderr = p.communicate()
|
||||
status = p.wait()
|
||||
if status != 0:
|
||||
raise CoreCommandError(status, args, stdout)
|
||||
raise CoreCommandError(status, args, stdout, stderr)
|
||||
return stdout.decode("utf-8").strip()
|
||||
else:
|
||||
return ""
|
||||
except OSError:
|
||||
raise CoreCommandError(-1, args)
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ import socket
|
|||
|
||||
from lxml import etree
|
||||
|
||||
from core import constants, utils
|
||||
from core import utils
|
||||
from core.constants import IP_BIN
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.base import CoreNodeBase
|
||||
|
@ -67,7 +68,7 @@ def get_address_type(address):
|
|||
def get_ipv4_addresses(hostname):
|
||||
if hostname == "localhost":
|
||||
addresses = []
|
||||
args = [constants.IP_BIN, "-o", "-f", "inet", "addr", "show"]
|
||||
args = "%s -o -f inet address show" % IP_BIN
|
||||
output = utils.check_cmd(args)
|
||||
for line in output.split(os.linesep):
|
||||
split = line.split()
|
||||
|
@ -106,10 +107,6 @@ class CoreXmlDeployment(object):
|
|||
def add_deployment(self):
|
||||
physical_host = self.add_physical_host(socket.gethostname())
|
||||
|
||||
# TODO: handle other servers
|
||||
# servers = self.session.broker.getservernames()
|
||||
# servers.remove("localhost")
|
||||
|
||||
for node_id in self.session.nodes:
|
||||
node = self.session.nodes[node_id]
|
||||
if isinstance(node, CoreNodeBase):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from lxml import etree
|
||||
|
||||
|
@ -44,19 +45,28 @@ def _value_to_params(value):
|
|||
return None
|
||||
|
||||
|
||||
def create_file(xml_element, doc_name, file_path):
|
||||
def create_file(xml_element, doc_name, file_path, server=None):
|
||||
"""
|
||||
Create xml file.
|
||||
|
||||
:param lxml.etree.Element xml_element: root element to write to file
|
||||
:param str doc_name: name to use in the emane doctype
|
||||
:param str file_path: file path to write xml file to
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
doctype = (
|
||||
'<!DOCTYPE %(doc_name)s SYSTEM "file:///usr/share/emane/dtd/%(doc_name)s.dtd">'
|
||||
% {"doc_name": doc_name}
|
||||
)
|
||||
if server is not None:
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
create_file(xml_element, doc_name, temp.name)
|
||||
temp.close()
|
||||
server.remote_put(temp.name, file_path)
|
||||
os.unlink(temp.name)
|
||||
else:
|
||||
corexml.write_xml_file(xml_element, file_path, doctype=doctype)
|
||||
|
||||
|
||||
|
@ -204,17 +214,18 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
|||
# increment nem id
|
||||
nem_id += 1
|
||||
|
||||
doc_name = "platform"
|
||||
for key in sorted(platform_xmls.keys()):
|
||||
platform_element = platform_xmls[key]
|
||||
if key == "host":
|
||||
file_name = "platform.xml"
|
||||
else:
|
||||
file_name = "platform%d.xml" % key
|
||||
|
||||
platform_element = platform_xmls[key]
|
||||
|
||||
doc_name = "platform"
|
||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||
create_file(platform_element, doc_name, file_path)
|
||||
else:
|
||||
file_name = "platform%d.xml" % key
|
||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||
linked_node = emane_manager.session.nodes[key]
|
||||
create_file(platform_element, doc_name, file_path, linked_node.server)
|
||||
|
||||
return nem_id
|
||||
|
||||
|
@ -303,15 +314,20 @@ def build_transport_xml(emane_manager, node, transport_type):
|
|||
file_name = transport_file_name(node.id, transport_type)
|
||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||
create_file(transport_element, doc_name, file_path)
|
||||
emane_manager.session.distributed.execute(
|
||||
lambda x: create_file(transport_element, doc_name, file_path, x)
|
||||
)
|
||||
|
||||
|
||||
def create_phy_xml(emane_model, config, file_path):
|
||||
def create_phy_xml(emane_model, config, file_path, server):
|
||||
"""
|
||||
Create the phy xml document.
|
||||
|
||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create xml
|
||||
:param dict config: all current configuration values
|
||||
:param str file_path: path to write file to
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
phy_element = etree.Element("phy", name="%s PHY" % emane_model.name)
|
||||
|
@ -322,15 +338,24 @@ def create_phy_xml(emane_model, config, file_path):
|
|||
phy_element, emane_model.phy_config, config, emane_model.config_ignore
|
||||
)
|
||||
create_file(phy_element, "phy", file_path)
|
||||
if server is not None:
|
||||
create_file(phy_element, "phy", file_path, server)
|
||||
else:
|
||||
create_file(phy_element, "phy", file_path)
|
||||
emane_model.session.distributed.execute(
|
||||
lambda x: create_file(phy_element, "phy", file_path, x)
|
||||
)
|
||||
|
||||
|
||||
def create_mac_xml(emane_model, config, file_path):
|
||||
def create_mac_xml(emane_model, config, file_path, server):
|
||||
"""
|
||||
Create the mac xml document.
|
||||
|
||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create xml
|
||||
:param dict config: all current configuration values
|
||||
:param str file_path: path to write file to
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
if not emane_model.mac_library:
|
||||
|
@ -343,10 +368,23 @@ def create_mac_xml(emane_model, config, file_path):
|
|||
mac_element, emane_model.mac_config, config, emane_model.config_ignore
|
||||
)
|
||||
create_file(mac_element, "mac", file_path)
|
||||
if server is not None:
|
||||
create_file(mac_element, "mac", file_path, server)
|
||||
else:
|
||||
create_file(mac_element, "mac", file_path)
|
||||
emane_model.session.distributed.execute(
|
||||
lambda x: create_file(mac_element, "mac", file_path, x)
|
||||
)
|
||||
|
||||
|
||||
def create_nem_xml(
|
||||
emane_model, config, nem_file, transport_definition, mac_definition, phy_definition
|
||||
emane_model,
|
||||
config,
|
||||
nem_file,
|
||||
transport_definition,
|
||||
mac_definition,
|
||||
phy_definition,
|
||||
server,
|
||||
):
|
||||
"""
|
||||
Create the nem xml document.
|
||||
|
@ -357,6 +395,8 @@ def create_nem_xml(
|
|||
:param str transport_definition: transport file definition path
|
||||
:param str mac_definition: mac file definition path
|
||||
:param str phy_definition: phy file definition path
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
nem_element = etree.Element("nem", name="%s NEM" % emane_model.name)
|
||||
|
@ -366,10 +406,16 @@ def create_nem_xml(
|
|||
etree.SubElement(nem_element, "transport", definition=transport_definition)
|
||||
etree.SubElement(nem_element, "mac", definition=mac_definition)
|
||||
etree.SubElement(nem_element, "phy", definition=phy_definition)
|
||||
if server is not None:
|
||||
create_file(nem_element, "nem", nem_file, server)
|
||||
else:
|
||||
create_file(nem_element, "nem", nem_file)
|
||||
emane_model.session.distributed.execute(
|
||||
lambda x: create_file(nem_element, "nem", nem_file, x)
|
||||
)
|
||||
|
||||
|
||||
def create_event_service_xml(group, port, device, file_directory):
|
||||
def create_event_service_xml(group, port, device, file_directory, server=None):
|
||||
"""
|
||||
Create a emane event service xml file.
|
||||
|
||||
|
@ -377,6 +423,8 @@ def create_event_service_xml(group, port, device, file_directory):
|
|||
:param str port: event port
|
||||
:param str device: event device
|
||||
:param str file_directory: directory to create file in
|
||||
:param core.emulator.distributed.DistributedServer server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
event_element = etree.Element("emaneeventmsgsvc")
|
||||
|
@ -391,7 +439,7 @@ def create_event_service_xml(group, port, device, file_directory):
|
|||
sub_element.text = value
|
||||
file_name = "libemaneeventservice.xml"
|
||||
file_path = os.path.join(file_directory, file_name)
|
||||
create_file(event_element, "emaneeventmsgsvc", file_path)
|
||||
create_file(event_element, "emaneeventmsgsvc", file_path, server)
|
||||
|
||||
|
||||
def transport_file_name(node_id, transport_type):
|
||||
|
|
|
@ -5,7 +5,7 @@ from core.emulator.emudata import IpPrefixes, NodeOptions
|
|||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
|
@ -14,7 +14,7 @@ if __name__ == "__main__":
|
|||
# create nodes and interfaces
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
options = NodeOptions(image="ubuntu")
|
||||
options = NodeOptions(image="ubuntu:18.04")
|
||||
|
||||
# create node one
|
||||
node_one = session.add_node(_type=NodeTypes.LXC, node_options=options)
|
||||
|
|
53
daemon/examples/python/distributed.py
Normal file
53
daemon/examples/python/distributed.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import logging
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
|
||||
def main():
|
||||
address = sys.argv[1]
|
||||
remote = sys.argv[2]
|
||||
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"controlnet": "172.16.0.0/24", "distributed_address": address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, remote)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
node_one = session.add_node()
|
||||
switch = session.add_node(_type=NodeTypes.SWITCH)
|
||||
options = NodeOptions()
|
||||
options.emulation_server = server_name
|
||||
node_two = session.add_node(node_options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface_one = prefixes.create_interface(node_one)
|
||||
interface_two = prefixes.create_interface(node_two)
|
||||
session.add_link(node_one.id, switch.id, interface_one=interface_one)
|
||||
session.add_link(node_two.id, switch.id, interface_one=interface_two)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
pdb.set_trace()
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
65
daemon/examples/python/distributed_emane.py
Normal file
65
daemon/examples/python/distributed_emane.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import logging
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
|
||||
def main():
|
||||
address = sys.argv[1]
|
||||
remote = sys.argv[2]
|
||||
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu(
|
||||
{
|
||||
"controlnet": "core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 "
|
||||
"core4:172.16.4.0/24 core5:172.16.5.0/24",
|
||||
"distributed_address": address,
|
||||
}
|
||||
)
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, remote)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
options = NodeOptions(model="mdr")
|
||||
options.set_position(0, 0)
|
||||
node_one = session.add_node(node_options=options)
|
||||
emane_net = session.add_node(_type=NodeTypes.EMANE)
|
||||
session.emane.set_model(emane_net, EmaneIeee80211abgModel)
|
||||
options.emulation_server = server_name
|
||||
node_two = session.add_node(node_options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface_one = prefixes.create_interface(node_one)
|
||||
interface_two = prefixes.create_interface(node_two)
|
||||
session.add_link(node_one.id, emane_net.id, interface_one=interface_one)
|
||||
session.add_link(node_two.id, emane_net.id, interface_one=interface_two)
|
||||
|
||||
# instantiate session
|
||||
try:
|
||||
session.instantiate()
|
||||
except Exception:
|
||||
logging.exception("error during instantiate")
|
||||
|
||||
# pause script for verification
|
||||
pdb.set_trace()
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
51
daemon/examples/python/distributed_lxd.py
Normal file
51
daemon/examples/python/distributed_lxd.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
import logging
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
|
||||
def main():
|
||||
address = sys.argv[1]
|
||||
remote = sys.argv[2]
|
||||
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"distributed_address": address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, remote)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
options = NodeOptions(image="ubuntu:18.04")
|
||||
node_one = session.add_node(_type=NodeTypes.LXC, node_options=options)
|
||||
options.emulation_server = server_name
|
||||
node_two = session.add_node(_type=NodeTypes.LXC, node_options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface_one = prefixes.create_interface(node_one)
|
||||
interface_two = prefixes.create_interface(node_two)
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
pdb.set_trace()
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
51
daemon/examples/python/distributed_ptp.py
Normal file
51
daemon/examples/python/distributed_ptp.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
import logging
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes
|
||||
|
||||
|
||||
def main():
|
||||
address = sys.argv[1]
|
||||
remote = sys.argv[2]
|
||||
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"distributed_address": address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, remote)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
options = NodeOptions()
|
||||
node_one = session.add_node(node_options=options)
|
||||
options.emulation_server = server_name
|
||||
node_two = session.add_node(node_options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface_one = prefixes.create_interface(node_one)
|
||||
interface_two = prefixes.create_interface(node_two)
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
pdb.set_trace()
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
43
daemon/examples/python/distributed_switches.py
Normal file
43
daemon/examples/python/distributed_switches.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import logging
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
|
||||
def main():
|
||||
address = sys.argv[1]
|
||||
remote = sys.argv[2]
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"distributed_address": address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, remote)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
switch_one = session.add_node(_type=NodeTypes.SWITCH)
|
||||
switch_two = session.add_node(_type=NodeTypes.SWITCH)
|
||||
|
||||
# create node interfaces and link
|
||||
session.add_link(switch_one.id, switch_two.id)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
pdb.set_trace()
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
56
daemon/examples/python/distributed_wlan.py
Normal file
56
daemon/examples/python/distributed_wlan.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import logging
|
||||
import pdb
|
||||
import sys
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
|
||||
|
||||
def main():
|
||||
address = sys.argv[1]
|
||||
remote = sys.argv[2]
|
||||
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"distributed_address": address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, remote)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
options = NodeOptions()
|
||||
options.set_position(0, 0)
|
||||
options.emulation_server = server_name
|
||||
node_one = session.add_node(node_options=options)
|
||||
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
session.mobility.set_model(wlan, BasicRangeModel)
|
||||
node_two = session.add_node(node_options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface_one = prefixes.create_interface(node_one)
|
||||
interface_two = prefixes.create_interface(node_two)
|
||||
session.add_link(node_one.id, wlan.id, interface_one=interface_one)
|
||||
session.add_link(node_two.id, wlan.id, interface_one=interface_two)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
pdb.set_trace()
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
main()
|
|
@ -40,10 +40,6 @@ def example(options):
|
|||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# start a shell on the first node
|
||||
node = session.get_node(2)
|
||||
node.client.term("bash")
|
||||
|
||||
# shutdown session
|
||||
input("press enter to exit...")
|
||||
coreemu.shutdown()
|
||||
|
|
|
@ -43,11 +43,14 @@ def example(options):
|
|||
last_node = session.get_node(options.nodes + 1)
|
||||
|
||||
print("starting iperf server on node: %s" % first_node.name)
|
||||
first_node.cmd(["iperf", "-s", "-D"])
|
||||
first_node.node_net_cmd("iperf -s -D")
|
||||
first_node_address = prefixes.ip4_address(first_node)
|
||||
print("node %s connecting to %s" % (last_node.name, first_node_address))
|
||||
last_node.client.icmd(["iperf", "-t", str(options.time), "-c", first_node_address])
|
||||
first_node.cmd(["killall", "-9", "iperf"])
|
||||
output = last_node.node_net_cmd(
|
||||
"iperf -t %s -c %s" % (options.time, first_node_address)
|
||||
)
|
||||
print(output)
|
||||
first_node.node_net_cmd("killall -9 iperf")
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
|
|
@ -47,11 +47,11 @@ def example(options):
|
|||
last_node = session.get_node(options.nodes + 1)
|
||||
|
||||
print("starting iperf server on node: %s" % first_node.name)
|
||||
first_node.cmd(["iperf", "-s", "-D"])
|
||||
first_node.node_net_cmd("iperf -s -D")
|
||||
address = prefixes.ip4_address(first_node)
|
||||
print("node %s connecting to %s" % (last_node.name, address))
|
||||
last_node.client.icmd(["iperf", "-t", str(options.time), "-c", address])
|
||||
first_node.cmd(["killall", "-9", "iperf"])
|
||||
last_node.node_net_cmd("iperf -t %s -c %s" % (options.time, address))
|
||||
first_node.node_net_cmd("killall -9 iperf")
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
configparser==4.0.2
|
||||
fabric==2.5.0
|
||||
future==0.17.1
|
||||
grpcio==1.23.0
|
||||
grpcio-tools==1.21.1
|
||||
invoke==1.3.0
|
||||
lxml==4.4.1
|
||||
protobuf==3.9.1
|
||||
six==1.12.0
|
||||
|
|
|
@ -35,8 +35,10 @@ setup(
|
|||
packages=find_packages(),
|
||||
install_requires=[
|
||||
"configparser",
|
||||
"fabric",
|
||||
"future",
|
||||
"grpcio",
|
||||
"invoke",
|
||||
"lxml",
|
||||
"protobuf",
|
||||
],
|
||||
|
|
|
@ -58,7 +58,6 @@ class CoreServerTest(object):
|
|||
self.request_handler = CoreHandler(request_mock, "", self.server)
|
||||
self.request_handler.session = self.session
|
||||
self.request_handler.add_session_handlers()
|
||||
self.session.broker.session_clients.append(self.request_handler)
|
||||
|
||||
# have broker handle a configuration state change
|
||||
self.session.set_state(EventTypes.DEFINITION_STATE)
|
||||
|
|
|
@ -12,7 +12,7 @@ from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
|||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emane.tdma import EmaneTdmaModel
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.errors import CoreError
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
|
||||
_EMANE_MODELS = [
|
||||
EmaneIeee80211abgModel,
|
||||
|
@ -26,7 +26,12 @@ _DIR = os.path.dirname(os.path.abspath(__file__))
|
|||
|
||||
def ping(from_node, to_node, ip_prefixes, count=3):
|
||||
address = ip_prefixes.ip4_address(to_node)
|
||||
return from_node.cmd(["ping", "-c", str(count), address])
|
||||
try:
|
||||
from_node.node_net_cmd("ping -c %s %s" % (count, address))
|
||||
status = 0
|
||||
except CoreCommandError as e:
|
||||
status = e.returncode
|
||||
return status
|
||||
|
||||
|
||||
class TestEmane:
|
||||
|
|
|
@ -3,42 +3,28 @@ Unit tests for testing basic CORE networks.
|
|||
"""
|
||||
|
||||
import os
|
||||
import stat
|
||||
import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.emulator.enumerations import MessageFlags, NodeTypes
|
||||
from core.errors import CoreCommandError
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
from core.nodes.client import VnodeClient
|
||||
|
||||
_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
|
||||
_WIRED = [NodeTypes.PEER_TO_PEER, NodeTypes.HUB, NodeTypes.SWITCH]
|
||||
|
||||
|
||||
def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None):
|
||||
"""
|
||||
Create clients
|
||||
|
||||
:param str sessiondir: session directory to create clients
|
||||
:param class clientcls: class to create clients from
|
||||
:param func cmdchnlfilterfunc: command channel filter function
|
||||
:return: list of created clients
|
||||
:rtype: list
|
||||
"""
|
||||
direntries = map(lambda x: os.path.join(sessiondir, x), os.listdir(sessiondir))
|
||||
cmdchnls = list(filter(lambda x: stat.S_ISSOCK(os.stat(x).st_mode), direntries))
|
||||
if cmdchnlfilterfunc:
|
||||
cmdchnls = list(filter(cmdchnlfilterfunc, cmdchnls))
|
||||
cmdchnls.sort()
|
||||
return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls)
|
||||
|
||||
|
||||
def ping(from_node, to_node, ip_prefixes):
|
||||
address = ip_prefixes.ip4_address(to_node)
|
||||
return from_node.cmd(["ping", "-c", "3", address])
|
||||
try:
|
||||
from_node.node_net_cmd("ping -c 3 %s" % address)
|
||||
status = 0
|
||||
except CoreCommandError as e:
|
||||
status = e.returncode
|
||||
return status
|
||||
|
||||
|
||||
class TestCore:
|
||||
|
@ -100,25 +86,8 @@ class TestCore:
|
|||
# check we are connected
|
||||
assert client.connected()
|
||||
|
||||
# check various command using vcmd module
|
||||
command = ["ls"]
|
||||
assert not client.cmd(command)
|
||||
status, output = client.cmd_output(command)
|
||||
assert not status
|
||||
p, stdin, stdout, stderr = client.popen(command)
|
||||
assert not p.wait()
|
||||
assert not client.icmd(command)
|
||||
|
||||
# check various command using command line
|
||||
assert not client.cmd(command)
|
||||
status, output = client.cmd_output(command)
|
||||
assert not status
|
||||
p, stdin, stdout, stderr = client.popen(command)
|
||||
assert not p.wait()
|
||||
assert not client.icmd(command)
|
||||
|
||||
# check module methods
|
||||
assert createclients(session.session_dir)
|
||||
# validate command
|
||||
assert client.check_cmd("echo hello") == "hello"
|
||||
|
||||
def test_netif(self, session, ip_prefixes):
|
||||
"""
|
||||
|
|
|
@ -763,13 +763,11 @@ class TestGui:
|
|||
(ConfigTlvs.VALUES, "%s:%s:%s" % (server, host, port)),
|
||||
],
|
||||
)
|
||||
coreserver.session.broker.addserver = mock.MagicMock()
|
||||
coreserver.session.broker.setupserver = mock.MagicMock()
|
||||
coreserver.session.distributed.add_server = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.session.broker.addserver.assert_called_once_with(server, host, port)
|
||||
coreserver.session.broker.setupserver.assert_called_once_with(server)
|
||||
coreserver.session.distributed.add_server.assert_called_once_with(server, host)
|
||||
|
||||
def test_config_services_request_all(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(
|
||||
|
|
|
@ -30,7 +30,7 @@ class TestNodes:
|
|||
assert os.path.exists(node.nodedir)
|
||||
assert node.alive()
|
||||
assert node.up
|
||||
assert node.check_cmd(["ip", "addr", "show", "lo"])
|
||||
assert node.node_net_cmd("ip address show lo")
|
||||
|
||||
def test_node_update(self, session):
|
||||
# given
|
||||
|
@ -67,4 +67,4 @@ class TestNodes:
|
|||
# then
|
||||
assert node
|
||||
assert node.up
|
||||
assert utils.check_cmd(["brctl", "show", node.brname])
|
||||
assert utils.check_cmd("brctl show %s" % node.brname)
|
||||
|
|
|
@ -19,7 +19,7 @@ array set g_node_types_default {
|
|||
4 {mdr mdr.gif mdr.gif {zebra OSPFv3MDR IPForward} \
|
||||
netns {built-in type for wireless routers}}
|
||||
5 {prouter router_green.gif router_green.gif \
|
||||
{zebra OSPFv2 OSPFv3 IPForward} \
|
||||
{} \
|
||||
physical {built-in type for physical nodes}}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,8 +117,10 @@ class CoreNs3Net(CoreNetworkBase):
|
|||
# icon used
|
||||
type = "wlan"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||
CoreNetworkBase.__init__(self, session, _id, name)
|
||||
def __init__(
|
||||
self, session, _id=None, name=None, start=True, server=None, policy=None
|
||||
):
|
||||
CoreNetworkBase.__init__(self, session, _id, name, start, server)
|
||||
self.tapbridge = ns.tap_bridge.TapBridgeHelper()
|
||||
self._ns3devs = {}
|
||||
self._tapdevs = {}
|
||||
|
|
Loading…
Add table
Reference in a new issue