Merge branch 'develop' into pydocupdates
This commit is contained in:
commit
2bfcc9ef24
100 changed files with 5340 additions and 3488 deletions
|
@ -46,8 +46,8 @@ MAINTAINERCLEANFILES = .version \
|
|||
|
||||
|
||||
if PYTHON3
|
||||
PYTHON_DEB_DEP = python3 >= 3.0
|
||||
PYTHON_RPM_DEP = python3 >= 3.0
|
||||
PYTHON_DEB_DEP = python3 >= 3.6
|
||||
PYTHON_RPM_DEP = python3 >= 3.6
|
||||
else
|
||||
PYTHON_DEB_DEP = python (>= 2.7), python (<< 3.0)
|
||||
PYTHON_RPM_DEP = python >= 2.7, python < 3.0
|
||||
|
|
23
daemon/.pre-commit-config.yaml
Normal file
23
daemon/.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort
|
||||
stages: [commit]
|
||||
language: system
|
||||
entry: bash -c 'cd daemon && pipenv run isort --atomic -y'
|
||||
types: [python]
|
||||
|
||||
- id: black
|
||||
name: black
|
||||
stages: [commit]
|
||||
language: system
|
||||
entry: bash -c 'cd daemon && pipenv run black --exclude ".+_pb2.*.py|doc|build|utm\.py" .'
|
||||
types: [python]
|
||||
|
||||
- id: flake8
|
||||
name: flake8
|
||||
stages: [commit]
|
||||
language: system
|
||||
entry: bash -c 'cd daemon && pipenv run flake8'
|
||||
types: [python]
|
20
daemon/Pipfile
Normal file
20
daemon/Pipfile
Normal file
|
@ -0,0 +1,20 @@
|
|||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[scripts]
|
||||
coredev = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
|
||||
coretest = "python -m pytest -v tests"
|
||||
|
||||
[dev-packages]
|
||||
grpcio-tools = "*"
|
||||
isort = "*"
|
||||
pre-commit = "*"
|
||||
flake8 = "*"
|
||||
black = "==19.3b0"
|
||||
pytest = "*"
|
||||
mock = "*"
|
||||
|
||||
[packages]
|
||||
core = {editable = true,path = "."}
|
460
daemon/Pipfile.lock
generated
Normal file
460
daemon/Pipfile.lock
generated
Normal file
|
@ -0,0 +1,460 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "d702e6eed5a1362bf261543572bbffd2e8a87140b8d8cb07b99fb0d25220a2b5"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"configparser": {
|
||||
"hashes": [
|
||||
"sha256:296a9c0d0607f689f2a262d4ca3fa2b22146ac0acb07fd281125c86dee3bcf50",
|
||||
"sha256:5e4c0d9de6ffc76f625eda1f5e28cec0700aed7cdacfe5070964df002ac02fec"
|
||||
],
|
||||
"version": "==4.0.1"
|
||||
},
|
||||
"core": {
|
||||
"editable": true,
|
||||
"path": "."
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
|
||||
],
|
||||
"version": "==0.17.1"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"version": "==1.23.0"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4",
|
||||
"sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc",
|
||||
"sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1",
|
||||
"sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046",
|
||||
"sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36",
|
||||
"sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5",
|
||||
"sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d",
|
||||
"sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916",
|
||||
"sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0",
|
||||
"sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27",
|
||||
"sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc",
|
||||
"sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7",
|
||||
"sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38",
|
||||
"sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5",
|
||||
"sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832",
|
||||
"sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a",
|
||||
"sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f",
|
||||
"sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9",
|
||||
"sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692",
|
||||
"sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84",
|
||||
"sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79",
|
||||
"sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"
|
||||
],
|
||||
"version": "==4.4.1"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"version": "==3.9.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
|
||||
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"aspy.yaml": {
|
||||
"hashes": [
|
||||
"sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc",
|
||||
"sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"atomicwrites": {
|
||||
"hashes": [
|
||||
"sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
|
||||
"sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||
],
|
||||
"version": "==19.1.0"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
|
||||
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==19.3b0"
|
||||
},
|
||||
"cfgv": {
|
||||
"hashes": [
|
||||
"sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144",
|
||||
"sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"
|
||||
],
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||
],
|
||||
"version": "==7.0"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
|
||||
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
|
||||
],
|
||||
"version": "==0.3"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548",
|
||||
"sha256:8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.7.8"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"version": "==1.23.0"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.23.0"
|
||||
},
|
||||
"identify": {
|
||||
"hashes": [
|
||||
"sha256:4f1fe9a59df4e80fcb0213086fcf502bc1765a01ea4fe8be48da3b65afd2a017",
|
||||
"sha256:d8919589bd2a5f99c66302fec0ef9027b12ae150b0b0213999ad3f695fc7296e"
|
||||
],
|
||||
"version": "==1.4.7"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:652234b6ab8f2506ae58e528b6fbcc668831d3cc758e1bc01ef438d328b68cdb",
|
||||
"sha256:6f264986fb88042bc1f0535fa9a557e6a376cfe5679dc77caac7fe8b5d43d05f"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==0.22"
|
||||
},
|
||||
"importlib-resources": {
|
||||
"hashes": [
|
||||
"sha256:6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b",
|
||||
"sha256:d3279fd0f6f847cced9f7acc19bd3e5df54d34f93a2e7bb5f238f81545787078"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
|
||||
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.21"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"mock": {
|
||||
"hashes": [
|
||||
"sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3",
|
||||
"sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.5"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
|
||||
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
|
||||
],
|
||||
"version": "==7.2.0"
|
||||
},
|
||||
"nodeenv": {
|
||||
"hashes": [
|
||||
"sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"
|
||||
],
|
||||
"version": "==1.3.3"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
|
||||
"sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
|
||||
],
|
||||
"version": "==19.1"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6",
|
||||
"sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"
|
||||
],
|
||||
"version": "==0.13.0"
|
||||
},
|
||||
"pre-commit": {
|
||||
"hashes": [
|
||||
"sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f",
|
||||
"sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.18.3"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"version": "==3.9.1"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
|
||||
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
|
||||
],
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
|
||||
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
|
||||
],
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
|
||||
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
|
||||
],
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
|
||||
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
|
||||
],
|
||||
"version": "==2.4.2"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:95d13143cc14174ca1a01ec68e84d76ba5d9d493ac02716fd9706c949a505210",
|
||||
"sha256:b78fe2881323bd44fd9bd76e5317173d4316577e7b1cddebae9136a4495ec865"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.1.2"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
|
||||
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
|
||||
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
|
||||
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
|
||||
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
|
||||
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
|
||||
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
|
||||
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
|
||||
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
|
||||
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
|
||||
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
|
||||
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
|
||||
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
|
||||
],
|
||||
"version": "==5.1.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"virtualenv": {
|
||||
"hashes": [
|
||||
"sha256:680af46846662bb38c5504b78bad9ed9e4f3ba2d54f54ba42494fdf94337fe30",
|
||||
"sha256:f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2"
|
||||
],
|
||||
"version": "==16.7.5"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||
],
|
||||
"version": "==0.1.7"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
|
||||
"sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,14 +9,17 @@ from core import constants
|
|||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def load_logging_config():
|
||||
def load_logging_config(config_path=None):
|
||||
"""
|
||||
Load CORE logging configuration file.
|
||||
|
||||
:param str config_path: path to logging config file,
|
||||
when None defaults to /etc/core/logging.conf
|
||||
:return: nothing
|
||||
"""
|
||||
log_config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
||||
with open(log_config_path, "r") as log_config_file:
|
||||
if not config_path:
|
||||
config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
||||
with open(config_path, "r") as log_config_file:
|
||||
log_config = json.load(log_config_file)
|
||||
logging.config.dictConfig(log_config)
|
||||
|
||||
|
@ -28,3 +31,11 @@ class CoreCommandError(subprocess.CalledProcessError):
|
|||
|
||||
def __str__(self):
|
||||
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
|
||||
|
||||
|
||||
class CoreError(Exception):
|
||||
"""
|
||||
Used for errors when dealing with CoreEmu and Sessions.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
|
@ -10,8 +10,7 @@ from contextlib import contextmanager
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc import core_pb2_grpc
|
||||
from core.api.grpc import core_pb2, core_pb2_grpc
|
||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
||||
|
||||
|
||||
|
@ -99,7 +98,7 @@ class InterfaceHelper(object):
|
|||
ip4mask=ip4_mask,
|
||||
ip6=ip6,
|
||||
ip6mask=ip6_mask,
|
||||
mac=str(mac)
|
||||
mac=str(mac),
|
||||
)
|
||||
|
||||
|
||||
|
@ -215,7 +214,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.SetSessionOptionsResponse
|
||||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.SetSessionOptionsRequest(session_id=session_id, config=config)
|
||||
request = core_pb2.SetSessionOptionsRequest(
|
||||
session_id=session_id, config=config
|
||||
)
|
||||
return self.stub.SetSessionOptions(request)
|
||||
|
||||
def get_session_location(self, session_id):
|
||||
|
@ -230,7 +231,17 @@ class CoreGrpcClient(object):
|
|||
request = core_pb2.GetSessionLocationRequest(session_id=session_id)
|
||||
return self.stub.GetSessionLocation(request)
|
||||
|
||||
def set_session_location(self, session_id, x=None, y=None, z=None, lat=None, lon=None, alt=None, scale=None):
|
||||
def set_session_location(
|
||||
self,
|
||||
session_id,
|
||||
x=None,
|
||||
y=None,
|
||||
z=None,
|
||||
lat=None,
|
||||
lon=None,
|
||||
alt=None,
|
||||
scale=None,
|
||||
):
|
||||
"""
|
||||
Set session location.
|
||||
|
||||
|
@ -247,7 +258,9 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||
request = core_pb2.SetSessionLocationRequest(session_id=session_id, position=position, scale=scale)
|
||||
request = core_pb2.SetSessionLocationRequest(
|
||||
session_id=session_id, position=position, scale=scale
|
||||
)
|
||||
return self.stub.SetSessionLocation(request)
|
||||
|
||||
def set_session_state(self, session_id, state):
|
||||
|
@ -324,7 +337,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.EditNodeResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.EditNodeRequest(session_id=session_id, node_id=node_id, position=position)
|
||||
request = core_pb2.EditNodeRequest(
|
||||
session_id=session_id, node_id=node_id, position=position
|
||||
)
|
||||
return self.stub.EditNode(request)
|
||||
|
||||
def delete_node(self, session_id, node_id):
|
||||
|
@ -350,7 +365,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.NodeCommandResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.NodeCommandRequest(session_id=session_id, node_id=node_id, command=command)
|
||||
request = core_pb2.NodeCommandRequest(
|
||||
session_id=session_id, node_id=node_id, command=command
|
||||
)
|
||||
return self.stub.NodeCommand(request)
|
||||
|
||||
def get_node_terminal(self, session_id, node_id):
|
||||
|
@ -363,7 +380,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.GetNodeTerminalResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetNodeTerminalRequest(session_id=session_id, node_id=node_id)
|
||||
request = core_pb2.GetNodeTerminalRequest(
|
||||
session_id=session_id, node_id=node_id
|
||||
)
|
||||
return self.stub.GetNodeTerminal(request)
|
||||
|
||||
def get_node_links(self, session_id, node_id):
|
||||
|
@ -379,7 +398,15 @@ class CoreGrpcClient(object):
|
|||
request = core_pb2.GetNodeLinksRequest(session_id=session_id, node_id=node_id)
|
||||
return self.stub.GetNodeLinks(request)
|
||||
|
||||
def add_link(self, session_id, node_one_id, node_two_id, interface_one=None, interface_two=None, options=None):
|
||||
def add_link(
|
||||
self,
|
||||
session_id,
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
interface_one=None,
|
||||
interface_two=None,
|
||||
options=None,
|
||||
):
|
||||
"""
|
||||
Add a link between nodes.
|
||||
|
||||
|
@ -394,12 +421,25 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session or one of the nodes don't exist
|
||||
"""
|
||||
link = core_pb2.Link(
|
||||
node_one_id=node_one_id, node_two_id=node_two_id, type=core_pb2.LinkType.WIRED,
|
||||
interface_one=interface_one, interface_two=interface_two, options=options)
|
||||
node_one_id=node_one_id,
|
||||
node_two_id=node_two_id,
|
||||
type=core_pb2.LinkType.WIRED,
|
||||
interface_one=interface_one,
|
||||
interface_two=interface_two,
|
||||
options=options,
|
||||
)
|
||||
request = core_pb2.AddLinkRequest(session_id=session_id, link=link)
|
||||
return self.stub.AddLink(request)
|
||||
|
||||
def edit_link(self, session_id, node_one_id, node_two_id, options, interface_one_id=None, interface_two_id=None):
|
||||
def edit_link(
|
||||
self,
|
||||
session_id,
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
options,
|
||||
interface_one_id=None,
|
||||
interface_two_id=None,
|
||||
):
|
||||
"""
|
||||
Edit a link between nodes.
|
||||
|
||||
|
@ -414,11 +454,23 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session or one of the nodes don't exist
|
||||
"""
|
||||
request = core_pb2.EditLinkRequest(
|
||||
session_id=session_id, node_one_id=node_one_id, node_two_id=node_two_id, options=options,
|
||||
interface_one_id=interface_one_id, interface_two_id=interface_two_id)
|
||||
session_id=session_id,
|
||||
node_one_id=node_one_id,
|
||||
node_two_id=node_two_id,
|
||||
options=options,
|
||||
interface_one_id=interface_one_id,
|
||||
interface_two_id=interface_two_id,
|
||||
)
|
||||
return self.stub.EditLink(request)
|
||||
|
||||
def delete_link(self, session_id, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None):
|
||||
def delete_link(
|
||||
self,
|
||||
session_id,
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
interface_one_id=None,
|
||||
interface_two_id=None,
|
||||
):
|
||||
"""
|
||||
Delete a link between nodes.
|
||||
|
||||
|
@ -432,8 +484,12 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.DeleteLinkRequest(
|
||||
session_id=session_id, node_one_id=node_one_id, node_two_id=node_two_id,
|
||||
interface_one_id=interface_one_id, interface_two_id=interface_two_id)
|
||||
session_id=session_id,
|
||||
node_one_id=node_one_id,
|
||||
node_two_id=node_two_id,
|
||||
interface_one_id=interface_one_id,
|
||||
interface_two_id=interface_two_id,
|
||||
)
|
||||
return self.stub.DeleteLink(request)
|
||||
|
||||
def get_hooks(self, session_id):
|
||||
|
@ -486,7 +542,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.GetMobilityConfigResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetMobilityConfigRequest(session_id=session_id, node_id=node_id)
|
||||
request = core_pb2.GetMobilityConfigRequest(
|
||||
session_id=session_id, node_id=node_id
|
||||
)
|
||||
return self.stub.GetMobilityConfig(request)
|
||||
|
||||
def set_mobility_config(self, session_id, node_id, config):
|
||||
|
@ -500,7 +558,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.SetMobilityConfigResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.SetMobilityConfigRequest(session_id=session_id, node_id=node_id, config=config)
|
||||
request = core_pb2.SetMobilityConfigRequest(
|
||||
session_id=session_id, node_id=node_id, config=config
|
||||
)
|
||||
return self.stub.SetMobilityConfig(request)
|
||||
|
||||
def mobility_action(self, session_id, node_id, action):
|
||||
|
@ -514,7 +574,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.MobilityActionResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.MobilityActionRequest(session_id=session_id, node_id=node_id, action=action)
|
||||
request = core_pb2.MobilityActionRequest(
|
||||
session_id=session_id, node_id=node_id, action=action
|
||||
)
|
||||
return self.stub.MobilityAction(request)
|
||||
|
||||
def get_services(self):
|
||||
|
@ -554,7 +616,9 @@ class CoreGrpcClient(object):
|
|||
services = service_defaults[node_type]
|
||||
default = core_pb2.ServiceDefaults(node_type=node_type, services=services)
|
||||
defaults.append(default)
|
||||
request = core_pb2.SetServiceDefaultsRequest(session_id=session_id, defaults=defaults)
|
||||
request = core_pb2.SetServiceDefaultsRequest(
|
||||
session_id=session_id, defaults=defaults
|
||||
)
|
||||
return self.stub.SetServiceDefaults(request)
|
||||
|
||||
def get_node_service(self, session_id, node_id, service):
|
||||
|
@ -568,7 +632,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.GetNodeServiceResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetNodeServiceRequest(session_id=session_id, node_id=node_id, service=service)
|
||||
request = core_pb2.GetNodeServiceRequest(
|
||||
session_id=session_id, node_id=node_id, service=service
|
||||
)
|
||||
return self.stub.GetNodeService(request)
|
||||
|
||||
def get_node_service_file(self, session_id, node_id, service, file_name):
|
||||
|
@ -584,10 +650,13 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetNodeServiceFileRequest(
|
||||
session_id=session_id, node_id=node_id, service=service, file=file_name)
|
||||
session_id=session_id, node_id=node_id, service=service, file=file_name
|
||||
)
|
||||
return self.stub.GetNodeServiceFile(request)
|
||||
|
||||
def set_node_service(self, session_id, node_id, service, startup, validate, shutdown):
|
||||
def set_node_service(
|
||||
self, session_id, node_id, service, startup, validate, shutdown
|
||||
):
|
||||
"""
|
||||
Set service data for a node.
|
||||
|
||||
|
@ -602,8 +671,13 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.SetNodeServiceRequest(
|
||||
session_id=session_id, node_id=node_id, service=service, startup=startup, validate=validate,
|
||||
shutdown=shutdown)
|
||||
session_id=session_id,
|
||||
node_id=node_id,
|
||||
service=service,
|
||||
startup=startup,
|
||||
validate=validate,
|
||||
shutdown=shutdown,
|
||||
)
|
||||
return self.stub.SetNodeService(request)
|
||||
|
||||
def set_node_service_file(self, session_id, node_id, service, file_name, data):
|
||||
|
@ -620,7 +694,12 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.SetNodeServiceFileRequest(
|
||||
session_id=session_id, node_id=node_id, service=service, file=file_name, data=data)
|
||||
session_id=session_id,
|
||||
node_id=node_id,
|
||||
service=service,
|
||||
file=file_name,
|
||||
data=data,
|
||||
)
|
||||
return self.stub.SetNodeServiceFile(request)
|
||||
|
||||
def service_action(self, session_id, node_id, service, action):
|
||||
|
@ -635,7 +714,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.ServiceActionResponse
|
||||
:raises grpc.RpcError: when session or node doesn't exist
|
||||
"""
|
||||
request = core_pb2.ServiceActionRequest(session_id=session_id, node_id=node_id, service=service, action=action)
|
||||
request = core_pb2.ServiceActionRequest(
|
||||
session_id=session_id, node_id=node_id, service=service, action=action
|
||||
)
|
||||
return self.stub.ServiceAction(request)
|
||||
|
||||
def get_wlan_config(self, session_id, node_id):
|
||||
|
@ -662,7 +743,9 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.SetWlanConfigResponse
|
||||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.SetWlanConfigRequest(session_id=session_id, node_id=node_id, config=config)
|
||||
request = core_pb2.SetWlanConfigRequest(
|
||||
session_id=session_id, node_id=node_id, config=config
|
||||
)
|
||||
return self.stub.SetWlanConfig(request)
|
||||
|
||||
def get_emane_config(self, session_id):
|
||||
|
@ -715,10 +798,13 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetEmaneModelConfigRequest(
|
||||
session_id=session_id, node_id=node_id, model=model, interface=interface_id)
|
||||
session_id=session_id, node_id=node_id, model=model, interface=interface_id
|
||||
)
|
||||
return self.stub.GetEmaneModelConfig(request)
|
||||
|
||||
def set_emane_model_config(self, session_id, node_id, model, config, interface_id=-1):
|
||||
def set_emane_model_config(
|
||||
self, session_id, node_id, model, config, interface_id=-1
|
||||
):
|
||||
"""
|
||||
Set emane model configuration for a node or a node's interface.
|
||||
|
||||
|
@ -732,7 +818,12 @@ class CoreGrpcClient(object):
|
|||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.SetEmaneModelConfigRequest(
|
||||
session_id=session_id, node_id=node_id, model=model, config=config, interface_id=interface_id)
|
||||
session_id=session_id,
|
||||
node_id=node_id,
|
||||
model=model,
|
||||
config=config,
|
||||
interface_id=interface_id,
|
||||
)
|
||||
return self.stub.SetEmaneModelConfig(request)
|
||||
|
||||
def get_emane_model_configs(self, session_id):
|
||||
|
|
|
@ -4,17 +4,24 @@ import os
|
|||
import re
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import grpc
|
||||
from builtins import int
|
||||
from concurrent import futures
|
||||
from queue import Queue, Empty
|
||||
from queue import Empty, Queue
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc import core_pb2_grpc
|
||||
from core.emulator.data import NodeData, LinkData, EventData, ConfigData, ExceptionData, FileData
|
||||
from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes, LinkTypes
|
||||
import grpc
|
||||
|
||||
from core import CoreError
|
||||
from core.api.grpc import core_pb2, core_pb2_grpc
|
||||
from core.emulator.data import (
|
||||
ConfigData,
|
||||
EventData,
|
||||
ExceptionData,
|
||||
FileData,
|
||||
LinkData,
|
||||
NodeData,
|
||||
)
|
||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, LinkTypes, NodeTypes
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
|
@ -50,8 +57,10 @@ def get_config_groups(config, configurable_options):
|
|||
for config_group in configurable_options.config_groups():
|
||||
start = config_group.start - 1
|
||||
stop = config_group.stop
|
||||
options = config_options[start: stop]
|
||||
config_group_proto = core_pb2.ConfigGroup(name=config_group.name, options=options)
|
||||
options = config_options[start:stop]
|
||||
config_group_proto = core_pb2.ConfigGroup(
|
||||
name=config_group.name, options=options
|
||||
)
|
||||
groups.append(config_group_proto)
|
||||
|
||||
return groups
|
||||
|
@ -87,9 +96,14 @@ def convert_link(session, link_data):
|
|||
interface = node.netif(link_data.interface1_id)
|
||||
interface_name = interface.name
|
||||
interface_one = core_pb2.Interface(
|
||||
id=link_data.interface1_id, name=interface_name, mac=convert_value(link_data.interface1_mac),
|
||||
ip4=convert_value(link_data.interface1_ip4), ip4mask=link_data.interface1_ip4_mask,
|
||||
ip6=convert_value(link_data.interface1_ip6), ip6mask=link_data.interface1_ip6_mask)
|
||||
id=link_data.interface1_id,
|
||||
name=interface_name,
|
||||
mac=convert_value(link_data.interface1_mac),
|
||||
ip4=convert_value(link_data.interface1_ip4),
|
||||
ip4mask=link_data.interface1_ip4_mask,
|
||||
ip6=convert_value(link_data.interface1_ip6),
|
||||
ip6mask=link_data.interface1_ip6_mask,
|
||||
)
|
||||
|
||||
interface_two = None
|
||||
if link_data.interface2_id is not None:
|
||||
|
@ -99,9 +113,14 @@ def convert_link(session, link_data):
|
|||
interface = node.netif(link_data.interface2_id)
|
||||
interface_name = interface.name
|
||||
interface_two = core_pb2.Interface(
|
||||
id=link_data.interface2_id, name=interface_name, mac=convert_value(link_data.interface2_mac),
|
||||
ip4=convert_value(link_data.interface2_ip4), ip4mask=link_data.interface2_ip4_mask,
|
||||
ip6=convert_value(link_data.interface2_ip6), ip6mask=link_data.interface2_ip6_mask)
|
||||
id=link_data.interface2_id,
|
||||
name=interface_name,
|
||||
mac=convert_value(link_data.interface2_mac),
|
||||
ip4=convert_value(link_data.interface2_ip4),
|
||||
ip4mask=link_data.interface2_ip4_mask,
|
||||
ip6=convert_value(link_data.interface2_ip6),
|
||||
ip6mask=link_data.interface2_ip6_mask,
|
||||
)
|
||||
|
||||
options = core_pb2.LinkOptions(
|
||||
opaque=link_data.opaque,
|
||||
|
@ -114,12 +133,16 @@ def convert_link(session, link_data):
|
|||
burst=link_data.burst,
|
||||
delay=link_data.delay,
|
||||
dup=link_data.dup,
|
||||
unidirectional=link_data.unidirectional
|
||||
unidirectional=link_data.unidirectional,
|
||||
)
|
||||
|
||||
return core_pb2.Link(
|
||||
type=link_data.link_type, node_one_id=link_data.node1_id, node_two_id=link_data.node2_id,
|
||||
interface_one=interface_one, interface_two=interface_two, options=options
|
||||
type=link_data.link_type,
|
||||
node_one_id=link_data.node1_id,
|
||||
node_two_id=link_data.node2_id,
|
||||
interface_one=interface_one,
|
||||
interface_two=interface_two,
|
||||
options=options,
|
||||
)
|
||||
|
||||
|
||||
|
@ -158,7 +181,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
context.abort(grpc.StatusCode.CANCELLED, "server stopping")
|
||||
|
||||
def listen(self, address):
|
||||
logging.info("starting grpc api: %s", address)
|
||||
logging.info("CORE gRPC API listening on: %s", address)
|
||||
self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
|
||||
core_pb2_grpc.add_CoreApiServicer_to_server(self, self.server)
|
||||
self.server.add_insecure_port(address)
|
||||
|
@ -173,14 +196,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
def get_session(self, session_id, context):
|
||||
session = self.coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, "session {} not found".format(session_id))
|
||||
context.abort(
|
||||
grpc.StatusCode.NOT_FOUND, "session {} not found".format(session_id)
|
||||
)
|
||||
return session
|
||||
|
||||
def get_node(self, session, node_id, context):
|
||||
try:
|
||||
return session.get_node(node_id)
|
||||
except KeyError:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, "node {} not found".format(node_id))
|
||||
context.abort(
|
||||
grpc.StatusCode.NOT_FOUND, "node {} not found".format(node_id)
|
||||
)
|
||||
|
||||
def CreateSession(self, request, context):
|
||||
logging.debug("create session: %s", request)
|
||||
|
@ -188,7 +215,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
session.set_state(EventTypes.DEFINITION_STATE)
|
||||
session.location.setrefgeo(47.57917, -122.13232, 2.0)
|
||||
session.location.refscale = 150000.0
|
||||
return core_pb2.CreateSessionResponse(session_id=session.id, state=session.state)
|
||||
return core_pb2.CreateSessionResponse(
|
||||
session_id=session.id, state=session.state
|
||||
)
|
||||
|
||||
def DeleteSession(self, request, context):
|
||||
logging.debug("delete session: %s", request)
|
||||
|
@ -201,7 +230,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
for session_id in self.coreemu.sessions:
|
||||
session = self.coreemu.sessions[session_id]
|
||||
session_summary = core_pb2.SessionSummary(
|
||||
id=session_id, state=session.state, nodes=session.get_node_count())
|
||||
id=session_id, state=session.state, nodes=session.get_node_count()
|
||||
)
|
||||
sessions.append(session_summary)
|
||||
return core_pb2.GetSessionsResponse(sessions=sessions)
|
||||
|
||||
|
@ -211,13 +241,21 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
x, y, z = session.location.refxyz
|
||||
lat, lon, alt = session.location.refgeo
|
||||
position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||
return core_pb2.GetSessionLocationResponse(position=position, scale=session.location.refscale)
|
||||
return core_pb2.GetSessionLocationResponse(
|
||||
position=position, scale=session.location.refscale
|
||||
)
|
||||
|
||||
def SetSessionLocation(self, request, context):
|
||||
logging.debug("set session location: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
session.location.refxyz = (request.position.x, request.position.y, request.position.z)
|
||||
session.location.setrefgeo(request.position.lat, request.position.lon, request.position.alt)
|
||||
session.location.refxyz = (
|
||||
request.position.x,
|
||||
request.position.y,
|
||||
request.position.z,
|
||||
)
|
||||
session.location.setrefgeo(
|
||||
request.position.lat, request.position.lon, request.position.alt
|
||||
)
|
||||
session.location.refscale = request.scale
|
||||
return core_pb2.SetSessionLocationResponse(result=True)
|
||||
|
||||
|
@ -275,7 +313,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
|
||||
node_type = nodeutils.get_node_type(node.__class__).value
|
||||
model = getattr(node, "type", None)
|
||||
position = core_pb2.Position(x=node.position.x, y=node.position.y, z=node.position.z)
|
||||
position = core_pb2.Position(
|
||||
x=node.position.x, y=node.position.y, z=node.position.z
|
||||
)
|
||||
|
||||
services = getattr(node, "services", [])
|
||||
if services is None:
|
||||
|
@ -287,8 +327,14 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
emane_model = node.model.name
|
||||
|
||||
node_proto = core_pb2.Node(
|
||||
id=node.id, name=node.name, emane=emane_model, model=model,
|
||||
type=node_type, position=position, services=services)
|
||||
id=node.id,
|
||||
name=node.name,
|
||||
emane=emane_model,
|
||||
model=model,
|
||||
type=node_type,
|
||||
position=position,
|
||||
services=services,
|
||||
)
|
||||
if isinstance(node, (DockerNode, LxcNode)):
|
||||
node_proto.image = node.image
|
||||
nodes.append(node_proto)
|
||||
|
@ -348,23 +394,38 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
services = event.services or ""
|
||||
services = services.split("|")
|
||||
node_proto = core_pb2.Node(
|
||||
id=event.id, name=event.name, model=event.model, position=position, services=services)
|
||||
id=event.id,
|
||||
name=event.name,
|
||||
model=event.model,
|
||||
position=position,
|
||||
services=services,
|
||||
)
|
||||
return core_pb2.NodeEvent(node=node_proto)
|
||||
|
||||
def _handle_link_event(self, event):
|
||||
interface_one = None
|
||||
if event.interface1_id is not None:
|
||||
interface_one = core_pb2.Interface(
|
||||
id=event.interface1_id, name=event.interface1_name, mac=convert_value(event.interface1_mac),
|
||||
ip4=convert_value(event.interface1_ip4), ip4mask=event.interface1_ip4_mask,
|
||||
ip6=convert_value(event.interface1_ip6), ip6mask=event.interface1_ip6_mask)
|
||||
id=event.interface1_id,
|
||||
name=event.interface1_name,
|
||||
mac=convert_value(event.interface1_mac),
|
||||
ip4=convert_value(event.interface1_ip4),
|
||||
ip4mask=event.interface1_ip4_mask,
|
||||
ip6=convert_value(event.interface1_ip6),
|
||||
ip6mask=event.interface1_ip6_mask,
|
||||
)
|
||||
|
||||
interface_two = None
|
||||
if event.interface2_id is not None:
|
||||
interface_two = core_pb2.Interface(
|
||||
id=event.interface2_id, name=event.interface2_name, mac=convert_value(event.interface2_mac),
|
||||
ip4=convert_value(event.interface2_ip4), ip4mask=event.interface2_ip4_mask,
|
||||
ip6=convert_value(event.interface2_ip6), ip6mask=event.interface2_ip6_mask)
|
||||
id=event.interface2_id,
|
||||
name=event.interface2_name,
|
||||
mac=convert_value(event.interface2_mac),
|
||||
ip4=convert_value(event.interface2_ip4),
|
||||
ip4mask=event.interface2_ip4_mask,
|
||||
ip6=convert_value(event.interface2_ip6),
|
||||
ip6mask=event.interface2_ip6_mask,
|
||||
)
|
||||
|
||||
options = core_pb2.LinkOptions(
|
||||
opaque=event.opaque,
|
||||
|
@ -377,11 +438,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
burst=event.burst,
|
||||
delay=event.delay,
|
||||
dup=event.dup,
|
||||
unidirectional=event.unidirectional
|
||||
unidirectional=event.unidirectional,
|
||||
)
|
||||
link = core_pb2.Link(
|
||||
type=event.link_type, node_one_id=event.node1_id, node_two_id=event.node2_id,
|
||||
interface_one=interface_one, interface_two=interface_two, options=options)
|
||||
type=event.link_type,
|
||||
node_one_id=event.node1_id,
|
||||
node_two_id=event.node2_id,
|
||||
interface_one=interface_one,
|
||||
interface_two=interface_two,
|
||||
options=options,
|
||||
)
|
||||
return core_pb2.LinkEvent(message_type=event.message_type, link=link)
|
||||
|
||||
def _handle_session_event(self, event):
|
||||
|
@ -394,7 +460,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
name=event.name,
|
||||
data=event.data,
|
||||
time=event_time,
|
||||
session_id=event.session
|
||||
session_id=event.session,
|
||||
)
|
||||
|
||||
def _handle_config_event(self, event):
|
||||
|
@ -415,7 +481,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
interface=event.interface_number,
|
||||
network_id=event.network_id,
|
||||
opaque=event.opaque,
|
||||
data_types=event.data_types
|
||||
data_types=event.data_types,
|
||||
)
|
||||
|
||||
def _handle_exception_event(self, event):
|
||||
|
@ -426,7 +492,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
source=event.source,
|
||||
date=event.date,
|
||||
text=event.text,
|
||||
opaque=event.opaque
|
||||
opaque=event.opaque,
|
||||
)
|
||||
|
||||
def _handle_file_event(self, event):
|
||||
|
@ -440,7 +506,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
source=event.source,
|
||||
session_id=event.session,
|
||||
data=event.data,
|
||||
compressed_data=event.compressed_data
|
||||
compressed_data=event.compressed_data,
|
||||
)
|
||||
|
||||
def Throughputs(self, request, context):
|
||||
|
@ -460,21 +526,29 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
previous_rxtx = last_stats.get(key)
|
||||
if not previous_rxtx:
|
||||
continue
|
||||
rx_kbps = (current_rxtx["rx"] - previous_rxtx["rx"]) * 8.0 / interval
|
||||
tx_kbps = (current_rxtx["tx"] - previous_rxtx["tx"]) * 8.0 / interval
|
||||
rx_kbps = (
|
||||
(current_rxtx["rx"] - previous_rxtx["rx"]) * 8.0 / interval
|
||||
)
|
||||
tx_kbps = (
|
||||
(current_rxtx["tx"] - previous_rxtx["tx"]) * 8.0 / interval
|
||||
)
|
||||
throughput = rx_kbps + tx_kbps
|
||||
if key.startswith("veth"):
|
||||
key = key.split(".")
|
||||
node_id = int(_INTERFACE_REGEX.search(key[0]).group())
|
||||
interface_id = int(key[1])
|
||||
interface_throughput = throughputs_event.interface_throughputs.add()
|
||||
interface_throughput = (
|
||||
throughputs_event.interface_throughputs.add()
|
||||
)
|
||||
interface_throughput.node_id = node_id
|
||||
interface_throughput.interface_id = interface_id
|
||||
interface_throughput.throughput = throughput
|
||||
elif key.startswith("b."):
|
||||
try:
|
||||
node_id = int(key.split(".")[1])
|
||||
bridge_throughput = throughputs_event.bridge_throughputs.add()
|
||||
bridge_throughput = (
|
||||
throughputs_event.bridge_throughputs.add()
|
||||
)
|
||||
bridge_throughput.node_id = node_id
|
||||
bridge_throughput.throughput = throughput
|
||||
except ValueError:
|
||||
|
@ -527,8 +601,13 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
if interface.net:
|
||||
net_id = interface.net.id
|
||||
interface_proto = core_pb2.Interface(
|
||||
id=interface_id, netid=net_id, name=interface.name, mac=str(interface.hwaddr),
|
||||
mtu=interface.mtu, flowid=interface.flow_id)
|
||||
id=interface_id,
|
||||
netid=net_id,
|
||||
name=interface.name,
|
||||
mac=str(interface.hwaddr),
|
||||
mtu=interface.mtu,
|
||||
flowid=interface.flow_id,
|
||||
)
|
||||
interfaces.append(interface_proto)
|
||||
|
||||
emane_model = None
|
||||
|
@ -536,11 +615,19 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
emane_model = node.model.name
|
||||
|
||||
services = [x.name for x in getattr(node, "services", [])]
|
||||
position = core_pb2.Position(x=node.position.x, y=node.position.y, z=node.position.z)
|
||||
position = core_pb2.Position(
|
||||
x=node.position.x, y=node.position.y, z=node.position.z
|
||||
)
|
||||
node_type = nodeutils.get_node_type(node.__class__).value
|
||||
node_proto = core_pb2.Node(
|
||||
id=node.id, name=node.name, type=node_type, emane=emane_model, model=node.type, position=position,
|
||||
services=services)
|
||||
id=node.id,
|
||||
name=node.name,
|
||||
type=node_type,
|
||||
emane=emane_model,
|
||||
model=node.type,
|
||||
position=position,
|
||||
services=services,
|
||||
)
|
||||
if isinstance(node, (DockerNode, LxcNode)):
|
||||
node_proto.image = node.image
|
||||
|
||||
|
@ -558,7 +645,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
lon = request.position.lon
|
||||
alt = request.position.alt
|
||||
node_options.set_location(lat, lon, alt)
|
||||
result = session.update_node(node_id, node_options)
|
||||
result = True
|
||||
try:
|
||||
session.update_node(node_id, node_options)
|
||||
except CoreError:
|
||||
result = False
|
||||
return core_pb2.EditNodeResponse(result=result)
|
||||
|
||||
def DeleteNode(self, request, context):
|
||||
|
@ -660,7 +751,13 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
link_options.key = options_data.key
|
||||
link_options.opaque = options_data.opaque
|
||||
|
||||
session.add_link(node_one_id, node_two_id, interface_one, interface_two, link_options=link_options)
|
||||
session.add_link(
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
interface_one,
|
||||
interface_two,
|
||||
link_options=link_options,
|
||||
)
|
||||
return core_pb2.AddLinkResponse(result=True)
|
||||
|
||||
def EditLink(self, request, context):
|
||||
|
@ -683,7 +780,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
link_options.unidirectional = options_data.unidirectional
|
||||
link_options.key = options_data.key
|
||||
link_options.opaque = options_data.opaque
|
||||
session.update_link(node_one_id, node_two_id, interface_one_id, interface_two_id, link_options)
|
||||
session.update_link(
|
||||
node_one_id, node_two_id, interface_one_id, interface_two_id, link_options
|
||||
)
|
||||
return core_pb2.EditLinkResponse(result=True)
|
||||
|
||||
def DeleteLink(self, request, context):
|
||||
|
@ -693,7 +792,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
node_two_id = request.node_two_id
|
||||
interface_one_id = request.interface_one_id
|
||||
interface_two_id = request.interface_two_id
|
||||
session.delete_link(node_one_id, node_two_id, interface_one_id, interface_two_id)
|
||||
session.delete_link(
|
||||
node_one_id, node_two_id, interface_one_id, interface_two_id
|
||||
)
|
||||
return core_pb2.DeleteLinkResponse(result=True)
|
||||
|
||||
def GetHooks(self, request, context):
|
||||
|
@ -733,14 +834,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
def GetMobilityConfig(self, request, context):
|
||||
logging.debug("get mobility config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
config = session.mobility.get_model_config(request.node_id, Ns2ScriptedMobility.name)
|
||||
config = session.mobility.get_model_config(
|
||||
request.node_id, Ns2ScriptedMobility.name
|
||||
)
|
||||
groups = get_config_groups(config, Ns2ScriptedMobility)
|
||||
return core_pb2.GetMobilityConfigResponse(groups=groups)
|
||||
|
||||
def SetMobilityConfig(self, request, context):
|
||||
logging.debug("set mobility config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
session.mobility.set_model_config(request.node_id, Ns2ScriptedMobility.name, request.config)
|
||||
session.mobility.set_model_config(
|
||||
request.node_id, Ns2ScriptedMobility.name, request.config
|
||||
)
|
||||
return core_pb2.SetMobilityConfigResponse(result=True)
|
||||
|
||||
def MobilityAction(self, request, context):
|
||||
|
@ -773,7 +878,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
all_service_defaults = []
|
||||
for node_type in session.services.default_services:
|
||||
services = session.services.default_services[node_type]
|
||||
service_defaults = core_pb2.ServiceDefaults(node_type=node_type, services=services)
|
||||
service_defaults = core_pb2.ServiceDefaults(
|
||||
node_type=node_type, services=services
|
||||
)
|
||||
all_service_defaults.append(service_defaults)
|
||||
return core_pb2.GetServiceDefaultsResponse(defaults=all_service_defaults)
|
||||
|
||||
|
@ -782,13 +889,17 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
session = self.get_session(request.session_id, context)
|
||||
session.services.default_services.clear()
|
||||
for service_defaults in request.defaults:
|
||||
session.services.default_services[service_defaults.node_type] = service_defaults.services
|
||||
session.services.default_services[
|
||||
service_defaults.node_type
|
||||
] = service_defaults.services
|
||||
return core_pb2.SetServiceDefaultsResponse(result=True)
|
||||
|
||||
def GetNodeService(self, request, context):
|
||||
logging.debug("get node service: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
service = session.services.get_service(request.node_id, request.service, default_service=True)
|
||||
service = session.services.get_service(
|
||||
request.node_id, request.service, default_service=True
|
||||
)
|
||||
service_proto = core_pb2.NodeServiceData(
|
||||
executables=service.executables,
|
||||
dependencies=service.dependencies,
|
||||
|
@ -799,7 +910,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
validation_mode=service.validation_mode.value,
|
||||
validation_timer=service.validation_timer,
|
||||
shutdown=service.shutdown,
|
||||
meta=service.meta
|
||||
meta=service.meta,
|
||||
)
|
||||
return core_pb2.GetNodeServiceResponse(service=service_proto)
|
||||
|
||||
|
@ -814,7 +925,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
break
|
||||
if not service:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, "service not found")
|
||||
file_data = session.services.get_service_file(node, request.service, request.file)
|
||||
file_data = session.services.get_service_file(
|
||||
node, request.service, request.file
|
||||
)
|
||||
return core_pb2.GetNodeServiceFileResponse(data=file_data.data)
|
||||
|
||||
def SetNodeService(self, request, context):
|
||||
|
@ -830,7 +943,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
def SetNodeServiceFile(self, request, context):
|
||||
logging.debug("set node service file: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
session.services.set_service_file(request.node_id, request.service, request.file, request.data)
|
||||
session.services.set_service_file(
|
||||
request.node_id, request.service, request.file, request.data
|
||||
)
|
||||
return core_pb2.SetNodeServiceFileResponse(result=True)
|
||||
|
||||
def ServiceAction(self, request, context):
|
||||
|
@ -867,14 +982,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
def GetWlanConfig(self, request, context):
|
||||
logging.debug("get wlan config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
config = session.mobility.get_model_config(request.node_id, BasicRangeModel.name)
|
||||
config = session.mobility.get_model_config(
|
||||
request.node_id, BasicRangeModel.name
|
||||
)
|
||||
groups = get_config_groups(config, BasicRangeModel)
|
||||
return core_pb2.GetWlanConfigResponse(groups=groups)
|
||||
|
||||
def SetWlanConfig(self, request, context):
|
||||
logging.debug("set wlan config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
session.mobility.set_model_config(request.node_id, BasicRangeModel.name, request.config)
|
||||
session.mobility.set_model_config(
|
||||
request.node_id, BasicRangeModel.name, request.config
|
||||
)
|
||||
if session.state == EventTypes.RUNTIME_STATE.value:
|
||||
node = self.get_node(session, request.node_id, context)
|
||||
node.updatemodel(request.config)
|
||||
|
@ -970,7 +1089,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
def GetInterfaces(self, request, context):
|
||||
interfaces = []
|
||||
for interface in os.listdir("/sys/class/net"):
|
||||
if interface.startswith("b.") or interface.startswith("veth") or interface == "lo":
|
||||
if (
|
||||
interface.startswith("b.")
|
||||
or interface.startswith("veth")
|
||||
or interface == "lo"
|
||||
):
|
||||
continue
|
||||
interfaces.append(interface)
|
||||
return core_pb2.GetInterfacesResponse(interfaces=interfaces)
|
||||
|
|
|
@ -12,23 +12,25 @@ import threading
|
|||
|
||||
from core import utils
|
||||
from core.api.tlv import coreapi
|
||||
from core.nodes.base import CoreNodeBase, CoreNetworkBase
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.emulator.enumerations import ConfigFlags
|
||||
from core.emulator.enumerations import ConfigTlvs
|
||||
from core.emulator.enumerations import EventTlvs
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import FileTlvs
|
||||
from core.emulator.enumerations import LinkTlvs
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import NodeTlvs
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.emulator.enumerations import (
|
||||
ConfigDataTypes,
|
||||
ConfigFlags,
|
||||
ConfigTlvs,
|
||||
EventTlvs,
|
||||
EventTypes,
|
||||
ExecuteTlvs,
|
||||
FileTlvs,
|
||||
LinkTlvs,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTlvs,
|
||||
NodeTypes,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||
from core.nodes.interface import GreTap
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
from core.nodes.network import GreTapBridge
|
||||
from core.nodes.physical import PhysicalNode
|
||||
|
||||
|
@ -147,7 +149,12 @@ class CoreBroker(object):
|
|||
while len(self.servers) > 0:
|
||||
name, server = self.servers.popitem()
|
||||
if server.sock is not None:
|
||||
logging.info("closing connection with %s: %s:%s", name, server.host, server.port)
|
||||
logging.info(
|
||||
"closing connection with %s: %s:%s",
|
||||
name,
|
||||
server.host,
|
||||
server.port,
|
||||
)
|
||||
server.close()
|
||||
self.dorecvloop = False
|
||||
if self.recvthread is not None:
|
||||
|
@ -157,7 +164,7 @@ class CoreBroker(object):
|
|||
"""
|
||||
Reset to initial state.
|
||||
"""
|
||||
logging.info("clearing state")
|
||||
logging.debug("broker reset")
|
||||
self.nodemap_lock.acquire()
|
||||
self.nodemap.clear()
|
||||
for server in self.nodecounts:
|
||||
|
@ -208,14 +215,22 @@ class CoreBroker(object):
|
|||
r, _w, _x = select.select(rlist, [], [], 1.0)
|
||||
for sock in r:
|
||||
server = self.getserverbysock(sock)
|
||||
logging.info("attempting to receive from server: peer:%s remote:%s",
|
||||
server.sock.getpeername(), server.sock.getsockname())
|
||||
logging.info(
|
||||
"attempting to receive from server: peer:%s remote:%s",
|
||||
server.sock.getpeername(),
|
||||
server.sock.getsockname(),
|
||||
)
|
||||
if server is None:
|
||||
# servers may have changed; loop again
|
||||
continue
|
||||
rcvlen = self.recv(server)
|
||||
if rcvlen == 0:
|
||||
logging.info("connection with server(%s) closed: %s:%s", server.name, server.host, server.port)
|
||||
logging.info(
|
||||
"connection with server(%s) closed: %s:%s",
|
||||
server.name,
|
||||
server.host,
|
||||
server.port,
|
||||
)
|
||||
|
||||
def recv(self, server):
|
||||
"""
|
||||
|
@ -236,7 +251,9 @@ class CoreBroker(object):
|
|||
return 0
|
||||
|
||||
if len(msghdr) != coreapi.CoreMessage.header_len:
|
||||
logging.warning("warning: broker received not enough data len=%s", len(msghdr))
|
||||
logging.warning(
|
||||
"warning: broker received not enough data len=%s", len(msghdr)
|
||||
)
|
||||
return len(msghdr)
|
||||
|
||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
|
||||
|
@ -293,11 +310,17 @@ class CoreBroker(object):
|
|||
with self.servers_lock:
|
||||
server = self.servers.get(name)
|
||||
if server is not None:
|
||||
if host == server.host and port == server.port and server.sock is not None:
|
||||
if (
|
||||
host == server.host
|
||||
and port == server.port
|
||||
and server.sock is not None
|
||||
):
|
||||
# leave this socket connected
|
||||
return
|
||||
|
||||
logging.info("closing connection with %s @ %s:%s", name, server.host, server.port)
|
||||
logging.info(
|
||||
"closing connection with %s @ %s:%s", name, server.host, server.port
|
||||
)
|
||||
server.close()
|
||||
del self.servers[name]
|
||||
|
||||
|
@ -307,7 +330,9 @@ class CoreBroker(object):
|
|||
try:
|
||||
server.connect()
|
||||
except IOError:
|
||||
logging.exception("error connecting to server(%s): %s:%s", name, host, port)
|
||||
logging.exception(
|
||||
"error connecting to server(%s): %s:%s", name, host, port
|
||||
)
|
||||
if server.sock is not None:
|
||||
self.startrecvloop()
|
||||
self.servers[name] = server
|
||||
|
@ -328,7 +353,12 @@ class CoreBroker(object):
|
|||
logging.exception("error deleting server")
|
||||
|
||||
if server.sock is not None:
|
||||
logging.info("closing connection with %s @ %s:%s", server.name, server.host, server.port)
|
||||
logging.info(
|
||||
"closing connection with %s @ %s:%s",
|
||||
server.name,
|
||||
server.host,
|
||||
server.port,
|
||||
)
|
||||
server.close()
|
||||
|
||||
def getserverbyname(self, name):
|
||||
|
@ -414,16 +444,31 @@ class CoreBroker(object):
|
|||
remotenum = n2num
|
||||
|
||||
if key in self.tunnels.keys():
|
||||
logging.warning("tunnel with key %s (%s-%s) already exists!", key, n1num, n2num)
|
||||
logging.warning(
|
||||
"tunnel with key %s (%s-%s) already exists!", key, n1num, n2num
|
||||
)
|
||||
else:
|
||||
_id = key & ((1 << 16) - 1)
|
||||
logging.info("adding tunnel for %s-%s to %s with key %s", n1num, n2num, remoteip, key)
|
||||
logging.info(
|
||||
"adding tunnel for %s-%s to %s with key %s", n1num, n2num, remoteip, key
|
||||
)
|
||||
if localnum in self.physical_nodes:
|
||||
# no bridge is needed on physical nodes; use the GreTap directly
|
||||
gt = GreTap(node=None, name=None, session=self.session,
|
||||
remoteip=remoteip, key=key)
|
||||
gt = GreTap(
|
||||
node=None,
|
||||
name=None,
|
||||
session=self.session,
|
||||
remoteip=remoteip,
|
||||
key=key,
|
||||
)
|
||||
else:
|
||||
gt = self.session.create_node(cls=GreTapBridge, _id=_id, policy="ACCEPT", remoteip=remoteip, key=key)
|
||||
gt = self.session.create_node(
|
||||
cls=GreTapBridge,
|
||||
_id=_id,
|
||||
policy="ACCEPT",
|
||||
remoteip=remoteip,
|
||||
key=key,
|
||||
)
|
||||
gt.localnum = localnum
|
||||
gt.remotenum = remotenum
|
||||
self.tunnels[key] = gt
|
||||
|
@ -457,8 +502,13 @@ class CoreBroker(object):
|
|||
return None
|
||||
|
||||
server_interface = getattr(net, "serverintf", None)
|
||||
if nodeutils.is_node(net, NodeTypes.CONTROL_NET) and server_interface is not None:
|
||||
logging.warning("control networks with server interfaces do not need a tunnel")
|
||||
if (
|
||||
nodeutils.is_node(net, NodeTypes.CONTROL_NET)
|
||||
and server_interface is not None
|
||||
):
|
||||
logging.warning(
|
||||
"control networks with server interfaces do not need a tunnel"
|
||||
)
|
||||
return None
|
||||
|
||||
servers = self.getserversbynode(node_id)
|
||||
|
@ -491,12 +541,18 @@ class CoreBroker(object):
|
|||
myip = host
|
||||
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
|
||||
if key in self.tunnels.keys():
|
||||
logging.info("tunnel already exists, returning existing tunnel: %s", key)
|
||||
logging.info(
|
||||
"tunnel already exists, returning existing tunnel: %s", key
|
||||
)
|
||||
gt = self.tunnels[key]
|
||||
r.append(gt)
|
||||
continue
|
||||
logging.info("adding tunnel for net %s to %s with key %s", node_id, host, key)
|
||||
gt = GreTap(node=None, name=None, session=self.session, remoteip=host, key=key)
|
||||
logging.info(
|
||||
"adding tunnel for net %s to %s with key %s", node_id, host, key
|
||||
)
|
||||
gt = GreTap(
|
||||
node=None, name=None, session=self.session, remoteip=host, key=key
|
||||
)
|
||||
self.tunnels[key] = gt
|
||||
r.append(gt)
|
||||
# attaching to net will later allow gt to be destroyed
|
||||
|
@ -515,7 +571,9 @@ class CoreBroker(object):
|
|||
"""
|
||||
key = self.tunnelkey(n1num, n2num)
|
||||
try:
|
||||
logging.info("deleting tunnel between %s - %s with key: %s", n1num, n2num, key)
|
||||
logging.info(
|
||||
"deleting tunnel between %s - %s with key: %s", n1num, n2num, key
|
||||
)
|
||||
gt = self.tunnels.pop(key)
|
||||
except KeyError:
|
||||
gt = None
|
||||
|
@ -643,12 +701,19 @@ class CoreBroker(object):
|
|||
elif message.message_type == MessageTypes.CONFIG.value:
|
||||
# broadcast location and services configuration everywhere
|
||||
confobj = message.get_tlv(ConfigTlvs.OBJECT.value)
|
||||
if confobj == "location" or confobj == "services" or confobj == "session" or confobj == "all":
|
||||
if (
|
||||
confobj == "location"
|
||||
or confobj == "services"
|
||||
or confobj == "session"
|
||||
or confobj == "all"
|
||||
):
|
||||
servers = self.getservers()
|
||||
elif message.message_type == MessageTypes.FILE.value:
|
||||
# broadcast hook scripts and custom service files everywhere
|
||||
filetype = message.get_tlv(FileTlvs.TYPE.value)
|
||||
if filetype is not None and (filetype[:5] == "hook:" or filetype[:8] == "service:"):
|
||||
if filetype is not None and (
|
||||
filetype[:5] == "hook:" or filetype[:8] == "service:"
|
||||
):
|
||||
servers = self.getservers()
|
||||
if message.message_type == MessageTypes.LINK.value:
|
||||
# prepare a server list from two node numbers in link message
|
||||
|
@ -695,11 +760,19 @@ class CoreBroker(object):
|
|||
# server of its local name
|
||||
tlvdata = b""
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, "broker")
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, ConfigFlags.UPDATE.value)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, (ConfigDataTypes.STRING.value,))
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value,
|
||||
"%s:%s:%s" % (server.name, server.host, server.port))
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.SESSION.value, "%s" % self.session.id)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||
ConfigTlvs.TYPE.value, ConfigFlags.UPDATE.value
|
||||
)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||
ConfigTlvs.DATA_TYPES.value, (ConfigDataTypes.STRING.value,)
|
||||
)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||
ConfigTlvs.VALUES.value,
|
||||
"%s:%s:%s" % (server.name, server.host, server.port),
|
||||
)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||
ConfigTlvs.SESSION.value, "%s" % self.session.id
|
||||
)
|
||||
msg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
||||
server.sock.send(msg)
|
||||
|
||||
|
@ -760,7 +833,10 @@ class CoreBroker(object):
|
|||
if nodecls is None:
|
||||
logging.warning("broker unimplemented node type %s", nodetype)
|
||||
return handle_locally, servers
|
||||
if issubclass(nodecls, CoreNetworkBase) and nodetype != NodeTypes.WIRELESS_LAN.value:
|
||||
if (
|
||||
issubclass(nodecls, CoreNetworkBase)
|
||||
and nodetype != NodeTypes.WIRELESS_LAN.value
|
||||
):
|
||||
# network node replicated on all servers; could be optimized
|
||||
# don"t replicate WLANs, because ebtables rules won"t work
|
||||
servers = self.getservers()
|
||||
|
@ -810,7 +886,9 @@ class CoreBroker(object):
|
|||
|
||||
# determine link message destination using non-network nodes
|
||||
nn = message.node_numbers()
|
||||
logging.debug("checking link nodes (%s) with network nodes (%s)", nn, self.network_nodes)
|
||||
logging.debug(
|
||||
"checking link nodes (%s) with network nodes (%s)", nn, self.network_nodes
|
||||
)
|
||||
if nn[0] in self.network_nodes:
|
||||
if nn[1] in self.network_nodes:
|
||||
# two network nodes linked together - prevent loops caused by
|
||||
|
@ -854,7 +932,9 @@ class CoreBroker(object):
|
|||
if host is None:
|
||||
host = self.getlinkendpoint(message, localn == nn[0])
|
||||
|
||||
logging.debug("handle locally(%s) and local node(%s)", handle_locally, localn)
|
||||
logging.debug(
|
||||
"handle locally(%s) and local node(%s)", handle_locally, localn
|
||||
)
|
||||
if localn is None:
|
||||
message = self.addlinkendpoints(message, servers1, servers2)
|
||||
elif message.flags & MessageFlags.ADD.value:
|
||||
|
@ -889,10 +969,10 @@ class CoreBroker(object):
|
|||
if server.host is not None:
|
||||
ip2 = server.host
|
||||
break
|
||||
tlvdata = message.raw_message[coreapi.CoreMessage.header_len:]
|
||||
tlvdata = message.raw_message[coreapi.CoreMessage.header_len :]
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.OPAQUE.value, "%s:%s" % (ip1, ip2))
|
||||
newraw = coreapi.CoreLinkMessage.pack(message.flags, tlvdata)
|
||||
msghdr = newraw[:coreapi.CoreMessage.header_len]
|
||||
msghdr = newraw[: coreapi.CoreMessage.header_len]
|
||||
return coreapi.CoreLinkMessage(message.flags, msghdr, tlvdata)
|
||||
|
||||
def getlinkendpoint(self, msg, first_is_local):
|
||||
|
@ -934,10 +1014,12 @@ class CoreBroker(object):
|
|||
:return: should handle locally or not
|
||||
:rtype: bool
|
||||
"""
|
||||
hdr = msg[:coreapi.CoreMessage.header_len]
|
||||
hdr = msg[: coreapi.CoreMessage.header_len]
|
||||
msgtype, flags, _msglen = coreapi.CoreMessage.unpack_header(hdr)
|
||||
msgcls = coreapi.CLASS_MAP[msgtype]
|
||||
return self.handle_message(msgcls(flags, hdr, msg[coreapi.CoreMessage.header_len:]))
|
||||
return self.handle_message(
|
||||
msgcls(flags, hdr, msg[coreapi.CoreMessage.header_len :])
|
||||
)
|
||||
|
||||
def forwardmsg(self, message, servers):
|
||||
"""
|
||||
|
@ -957,9 +1039,19 @@ class CoreBroker(object):
|
|||
# local emulation server, handle this locally
|
||||
handle_locally = True
|
||||
elif server.sock is None:
|
||||
logging.info("server %s @ %s:%s is disconnected", server.name, server.host, server.port)
|
||||
logging.info(
|
||||
"server %s @ %s:%s is disconnected",
|
||||
server.name,
|
||||
server.host,
|
||||
server.port,
|
||||
)
|
||||
else:
|
||||
logging.info("forwarding message to server(%s): %s:%s", server.name, server.host, server.port)
|
||||
logging.info(
|
||||
"forwarding message to server(%s): %s:%s",
|
||||
server.name,
|
||||
server.host,
|
||||
server.port,
|
||||
)
|
||||
logging.debug("message being forwarded:\n%s", message)
|
||||
server.sock.send(message.raw_message)
|
||||
return handle_locally
|
||||
|
@ -986,7 +1078,10 @@ class CoreBroker(object):
|
|||
lhost, lport = None, None
|
||||
if server.sock:
|
||||
lhost, lport = server.sock.getsockname()
|
||||
f.write("%s %s %s %s %s\n" % (server.name, server.host, server.port, lhost, lport))
|
||||
f.write(
|
||||
"%s %s %s %s %s\n"
|
||||
% (server.name, server.host, server.port, lhost, lport)
|
||||
)
|
||||
except IOError:
|
||||
logging.exception("error writing server list to the file: %s", filename)
|
||||
|
||||
|
@ -1015,7 +1110,9 @@ class CoreBroker(object):
|
|||
with open(filename, "w") as f:
|
||||
f.write("%s\n%s\n" % (serverstr, nodestr))
|
||||
except IOError:
|
||||
logging.exception("error writing server file %s for node %s", filename, name)
|
||||
logging.exception(
|
||||
"error writing server file %s for node %s", filename, name
|
||||
)
|
||||
|
||||
def local_instantiation_complete(self):
|
||||
"""
|
||||
|
@ -1031,7 +1128,9 @@ class CoreBroker(object):
|
|||
|
||||
# broadcast out instantiate complete
|
||||
tlvdata = b""
|
||||
tlvdata += coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_COMPLETE.value)
|
||||
tlvdata += coreapi.CoreEventTlv.pack(
|
||||
EventTlvs.TYPE.value, EventTypes.INSTANTIATION_COMPLETE.value
|
||||
)
|
||||
message = coreapi.CoreEventMessage.pack(0, tlvdata)
|
||||
for session_client in self.session_clients:
|
||||
session_client.sendall(message)
|
||||
|
|
|
@ -7,26 +7,27 @@ CORE API messaging is leveraged for communication with the GUI.
|
|||
|
||||
import socket
|
||||
import struct
|
||||
from past.builtins import basestring
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from past.builtins import basestring
|
||||
|
||||
from core.api.tlv import structutils
|
||||
from core.emulator.enumerations import ConfigTlvs
|
||||
from core.emulator.enumerations import EventTlvs
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import ExceptionTlvs
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import FileTlvs
|
||||
from core.emulator.enumerations import InterfaceTlvs
|
||||
from core.emulator.enumerations import LinkTlvs
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import NodeTlvs
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.emulator.enumerations import SessionTlvs
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
from core.emulator.enumerations import (
|
||||
ConfigTlvs,
|
||||
EventTlvs,
|
||||
EventTypes,
|
||||
ExceptionTlvs,
|
||||
ExecuteTlvs,
|
||||
FileTlvs,
|
||||
InterfaceTlvs,
|
||||
LinkTlvs,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTlvs,
|
||||
RegisterTlvs,
|
||||
SessionTlvs,
|
||||
)
|
||||
from core.nodes.ipaddress import IpAddress, MacAddress
|
||||
|
||||
|
||||
class CoreTlvData(object):
|
||||
|
@ -139,6 +140,7 @@ class CoreTlvDataUint16(CoreTlvData):
|
|||
"""
|
||||
Helper class for packing uint16 data.
|
||||
"""
|
||||
|
||||
data_format = "!H"
|
||||
data_type = int
|
||||
pad_len = 0
|
||||
|
@ -148,6 +150,7 @@ class CoreTlvDataUint32(CoreTlvData):
|
|||
"""
|
||||
Helper class for packing uint32 data.
|
||||
"""
|
||||
|
||||
data_format = "!2xI"
|
||||
data_type = int
|
||||
pad_len = 2
|
||||
|
@ -157,6 +160,7 @@ class CoreTlvDataUint64(CoreTlvData):
|
|||
"""
|
||||
Helper class for packing uint64 data.
|
||||
"""
|
||||
|
||||
data_format = "!2xQ"
|
||||
data_type = int
|
||||
pad_len = 2
|
||||
|
@ -166,6 +170,7 @@ class CoreTlvDataString(CoreTlvData):
|
|||
"""
|
||||
Helper class for packing string data.
|
||||
"""
|
||||
|
||||
data_type = str
|
||||
|
||||
@classmethod
|
||||
|
@ -204,6 +209,7 @@ class CoreTlvDataUint16List(CoreTlvData):
|
|||
"""
|
||||
List of unsigned 16-bit values.
|
||||
"""
|
||||
|
||||
data_type = tuple
|
||||
data_format = "!H"
|
||||
|
||||
|
@ -253,6 +259,7 @@ class CoreTlvDataIpv4Addr(CoreTlvDataObj):
|
|||
"""
|
||||
Utility class for packing/unpacking Ipv4 addresses.
|
||||
"""
|
||||
|
||||
data_type = IpAddress.from_string
|
||||
data_format = "!2x4s"
|
||||
pad_len = 2
|
||||
|
@ -283,6 +290,7 @@ class CoreTlvDataIPv6Addr(CoreTlvDataObj):
|
|||
"""
|
||||
Utility class for packing/unpacking Ipv6 addresses.
|
||||
"""
|
||||
|
||||
data_format = "!16s2x"
|
||||
data_type = IpAddress.from_string
|
||||
pad_len = 2
|
||||
|
@ -313,6 +321,7 @@ class CoreTlvDataMacAddr(CoreTlvDataObj):
|
|||
"""
|
||||
Utility class for packing/unpacking mac addresses.
|
||||
"""
|
||||
|
||||
data_format = "!2x8s"
|
||||
data_type = MacAddress.from_string
|
||||
pad_len = 2
|
||||
|
@ -345,6 +354,7 @@ class CoreTlv(object):
|
|||
"""
|
||||
Base class for representing CORE TLVs.
|
||||
"""
|
||||
|
||||
header_format = "!BB"
|
||||
header_len = struct.calcsize(header_format)
|
||||
|
||||
|
@ -379,10 +389,12 @@ class CoreTlv(object):
|
|||
:param data: data to unpack
|
||||
:return: unpacked data class
|
||||
"""
|
||||
tlv_type, tlv_len = struct.unpack(cls.header_format, data[:cls.header_len])
|
||||
tlv_type, tlv_len = struct.unpack(cls.header_format, data[: cls.header_len])
|
||||
header_len = cls.header_len
|
||||
if tlv_len == 0:
|
||||
tlv_type, _zero, tlv_len = struct.unpack(cls.long_header_format, data[:cls.long_header_len])
|
||||
tlv_type, _zero, tlv_len = struct.unpack(
|
||||
cls.long_header_format, data[: cls.long_header_len]
|
||||
)
|
||||
header_len = cls.long_header_len
|
||||
tlv_size = header_len + tlv_len
|
||||
# for 32-bit alignment
|
||||
|
@ -435,7 +447,11 @@ class CoreTlv(object):
|
|||
:return: string representation
|
||||
:rtype: str
|
||||
"""
|
||||
return "%s <tlvtype = %s, value = %s>" % (self.__class__.__name__, self.type_str(), self.value)
|
||||
return "%s <tlvtype = %s, value = %s>" % (
|
||||
self.__class__.__name__,
|
||||
self.type_str(),
|
||||
self.value,
|
||||
)
|
||||
|
||||
|
||||
class CoreNodeTlv(CoreTlv):
|
||||
|
@ -686,14 +702,16 @@ class CoreMessage(object):
|
|||
:return: unpacked tuple
|
||||
:rtype: tuple
|
||||
"""
|
||||
message_type, message_flags, message_len = struct.unpack(cls.header_format, data[:cls.header_len])
|
||||
message_type, message_flags, message_len = struct.unpack(
|
||||
cls.header_format, data[: cls.header_len]
|
||||
)
|
||||
return message_type, message_flags, message_len
|
||||
|
||||
@classmethod
|
||||
def create(cls, flags, values):
|
||||
tlv_data = structutils.pack_values(cls.tlv_class, values)
|
||||
packed = cls.pack(flags, tlv_data)
|
||||
header_data = packed[:cls.header_len]
|
||||
header_data = packed[: cls.header_len]
|
||||
return cls(flags, header_data, tlv_data)
|
||||
|
||||
@classmethod
|
||||
|
@ -705,7 +723,9 @@ class CoreMessage(object):
|
|||
:param tlv_data: data to get length from for packing
|
||||
:return: combined header and tlv data
|
||||
"""
|
||||
header = struct.pack(cls.header_format, cls.message_type, message_flags, len(tlv_data))
|
||||
header = struct.pack(
|
||||
cls.header_format, cls.message_type, message_flags, len(tlv_data)
|
||||
)
|
||||
return header + tlv_data
|
||||
|
||||
def add_tlv_data(self, key, value):
|
||||
|
@ -807,7 +827,11 @@ class CoreMessage(object):
|
|||
:return: string representation
|
||||
:rtype: str
|
||||
"""
|
||||
result = "%s <msgtype = %s, flags = %s>" % (self.__class__.__name__, self.type_str(), self.flag_str())
|
||||
result = "%s <msgtype = %s, flags = %s>" % (
|
||||
self.__class__.__name__,
|
||||
self.type_str(),
|
||||
self.flag_str(),
|
||||
)
|
||||
|
||||
for key in self.tlv_data:
|
||||
value = self.tlv_data[key]
|
||||
|
@ -879,6 +903,7 @@ class CoreNodeMessage(CoreMessage):
|
|||
"""
|
||||
CORE node message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.NODE.value
|
||||
tlv_class = CoreNodeTlv
|
||||
|
||||
|
@ -887,6 +912,7 @@ class CoreLinkMessage(CoreMessage):
|
|||
"""
|
||||
CORE link message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.LINK.value
|
||||
tlv_class = CoreLinkTlv
|
||||
|
||||
|
@ -895,6 +921,7 @@ class CoreExecMessage(CoreMessage):
|
|||
"""
|
||||
CORE execute message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.EXECUTE.value
|
||||
tlv_class = CoreExecuteTlv
|
||||
|
||||
|
@ -903,6 +930,7 @@ class CoreRegMessage(CoreMessage):
|
|||
"""
|
||||
CORE register message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.REGISTER.value
|
||||
tlv_class = CoreRegisterTlv
|
||||
|
||||
|
@ -911,6 +939,7 @@ class CoreConfMessage(CoreMessage):
|
|||
"""
|
||||
CORE configuration message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.CONFIG.value
|
||||
tlv_class = CoreConfigTlv
|
||||
|
||||
|
@ -919,6 +948,7 @@ class CoreFileMessage(CoreMessage):
|
|||
"""
|
||||
CORE file message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.FILE.value
|
||||
tlv_class = CoreFileTlv
|
||||
|
||||
|
@ -927,6 +957,7 @@ class CoreIfaceMessage(CoreMessage):
|
|||
"""
|
||||
CORE interface message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.INTERFACE.value
|
||||
tlv_class = CoreInterfaceTlv
|
||||
|
||||
|
@ -935,6 +966,7 @@ class CoreEventMessage(CoreMessage):
|
|||
"""
|
||||
CORE event message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.EVENT.value
|
||||
tlv_class = CoreEventTlv
|
||||
|
||||
|
@ -943,6 +975,7 @@ class CoreSessionMessage(CoreMessage):
|
|||
"""
|
||||
CORE session message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.SESSION.value
|
||||
tlv_class = CoreSessionTlv
|
||||
|
||||
|
@ -951,6 +984,7 @@ class CoreExceptionMessage(CoreMessage):
|
|||
"""
|
||||
CORE exception message class.
|
||||
"""
|
||||
|
||||
message_type = MessageTypes.EXCEPTION.value
|
||||
tlv_class = CoreExceptionTlv
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@ class CoreServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
|||
TCP server class, manages sessions and spawns request handlers for
|
||||
incoming connections.
|
||||
"""
|
||||
|
||||
daemon_threads = True
|
||||
allow_reuse_address = True
|
||||
|
||||
|
@ -34,6 +35,7 @@ class CoreUdpServer(socketserver.ThreadingMixIn, socketserver.UDPServer):
|
|||
UDP server class, manages sessions and spawns request handlers for
|
||||
incoming connections.
|
||||
"""
|
||||
|
||||
daemon_threads = True
|
||||
allow_reuse_address = True
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ Converts CORE data objects into legacy API messages.
|
|||
"""
|
||||
|
||||
from core.api.tlv import coreapi, structutils
|
||||
from core.emulator.enumerations import ConfigTlvs
|
||||
from core.emulator.enumerations import NodeTlvs
|
||||
from core.emulator.enumerations import ConfigTlvs, NodeTlvs
|
||||
|
||||
|
||||
def convert_node(node_data):
|
||||
|
@ -14,28 +13,31 @@ def convert_node(node_data):
|
|||
:param core.emulator.data.NodeData node_data: node data to convert
|
||||
:return: packed node message
|
||||
"""
|
||||
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
|
||||
(NodeTlvs.NUMBER, node_data.id),
|
||||
(NodeTlvs.TYPE, node_data.node_type),
|
||||
(NodeTlvs.NAME, node_data.name),
|
||||
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
|
||||
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
|
||||
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
|
||||
(NodeTlvs.MODEL, node_data.model),
|
||||
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
|
||||
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
|
||||
(NodeTlvs.SESSION, node_data.session),
|
||||
(NodeTlvs.X_POSITION, node_data.x_position),
|
||||
(NodeTlvs.Y_POSITION, node_data.y_position),
|
||||
(NodeTlvs.CANVAS, node_data.canvas),
|
||||
(NodeTlvs.NETWORK_ID, node_data.network_id),
|
||||
(NodeTlvs.SERVICES, node_data.services),
|
||||
(NodeTlvs.LATITUDE, node_data.latitude),
|
||||
(NodeTlvs.LONGITUDE, node_data.longitude),
|
||||
(NodeTlvs.ALTITUDE, node_data.altitude),
|
||||
(NodeTlvs.ICON, node_data.icon),
|
||||
(NodeTlvs.OPAQUE, node_data.opaque)
|
||||
])
|
||||
tlv_data = structutils.pack_values(
|
||||
coreapi.CoreNodeTlv,
|
||||
[
|
||||
(NodeTlvs.NUMBER, node_data.id),
|
||||
(NodeTlvs.TYPE, node_data.node_type),
|
||||
(NodeTlvs.NAME, node_data.name),
|
||||
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
|
||||
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
|
||||
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
|
||||
(NodeTlvs.MODEL, node_data.model),
|
||||
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
|
||||
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
|
||||
(NodeTlvs.SESSION, node_data.session),
|
||||
(NodeTlvs.X_POSITION, node_data.x_position),
|
||||
(NodeTlvs.Y_POSITION, node_data.y_position),
|
||||
(NodeTlvs.CANVAS, node_data.canvas),
|
||||
(NodeTlvs.NETWORK_ID, node_data.network_id),
|
||||
(NodeTlvs.SERVICES, node_data.services),
|
||||
(NodeTlvs.LATITUDE, node_data.latitude),
|
||||
(NodeTlvs.LONGITUDE, node_data.longitude),
|
||||
(NodeTlvs.ALTITUDE, node_data.altitude),
|
||||
(NodeTlvs.ICON, node_data.icon),
|
||||
(NodeTlvs.OPAQUE, node_data.opaque),
|
||||
],
|
||||
)
|
||||
return coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)
|
||||
|
||||
|
||||
|
@ -46,19 +48,22 @@ def convert_config(config_data):
|
|||
:param core.emulator.data.ConfigData config_data: config data to convert
|
||||
:return: packed message
|
||||
"""
|
||||
tlv_data = structutils.pack_values(coreapi.CoreConfigTlv, [
|
||||
(ConfigTlvs.NODE, config_data.node),
|
||||
(ConfigTlvs.OBJECT, config_data.object),
|
||||
(ConfigTlvs.TYPE, config_data.type),
|
||||
(ConfigTlvs.DATA_TYPES, config_data.data_types),
|
||||
(ConfigTlvs.VALUES, config_data.data_values),
|
||||
(ConfigTlvs.CAPTIONS, config_data.captions),
|
||||
(ConfigTlvs.BITMAP, config_data.bitmap),
|
||||
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
|
||||
(ConfigTlvs.GROUPS, config_data.groups),
|
||||
(ConfigTlvs.SESSION, config_data.session),
|
||||
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
|
||||
(ConfigTlvs.NETWORK_ID, config_data.network_id),
|
||||
(ConfigTlvs.OPAQUE, config_data.opaque),
|
||||
])
|
||||
tlv_data = structutils.pack_values(
|
||||
coreapi.CoreConfigTlv,
|
||||
[
|
||||
(ConfigTlvs.NODE, config_data.node),
|
||||
(ConfigTlvs.OBJECT, config_data.object),
|
||||
(ConfigTlvs.TYPE, config_data.type),
|
||||
(ConfigTlvs.DATA_TYPES, config_data.data_types),
|
||||
(ConfigTlvs.VALUES, config_data.data_values),
|
||||
(ConfigTlvs.CAPTIONS, config_data.captions),
|
||||
(ConfigTlvs.BITMAP, config_data.bitmap),
|
||||
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
|
||||
(ConfigTlvs.GROUPS, config_data.groups),
|
||||
(ConfigTlvs.SESSION, config_data.session),
|
||||
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
|
||||
(ConfigTlvs.NETWORK_ID, config_data.network_id),
|
||||
(ConfigTlvs.OPAQUE, config_data.opaque),
|
||||
],
|
||||
)
|
||||
return coreapi.CoreConfMessage.pack(config_data.message_type, tlv_data)
|
||||
|
|
|
@ -3,6 +3,7 @@ Utilities for working with python struct data.
|
|||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from past.builtins import basestring
|
||||
|
||||
|
||||
|
|
|
@ -40,7 +40,11 @@ class ConfigShim(object):
|
|||
"""
|
||||
group_strings = []
|
||||
for config_group in config_groups:
|
||||
group_string = "%s:%s-%s" % (config_group.name, config_group.start, config_group.stop)
|
||||
group_string = "%s:%s-%s" % (
|
||||
config_group.name,
|
||||
config_group.start,
|
||||
config_group.stop,
|
||||
)
|
||||
group_strings.append(group_string)
|
||||
return "|".join(group_strings)
|
||||
|
||||
|
@ -96,7 +100,7 @@ class ConfigShim(object):
|
|||
captions=captions,
|
||||
possible_values="|".join(possible_values),
|
||||
bitmap=configurable_options.bitmap,
|
||||
groups=groups_str
|
||||
groups=groups_str,
|
||||
)
|
||||
|
||||
|
||||
|
@ -127,13 +131,19 @@ class Configuration(object):
|
|||
|
||||
def __str__(self):
|
||||
return "%s(id=%s, type=%s, default=%s, options=%s)" % (
|
||||
self.__class__.__name__, self.id, self.type, self.default, self.options)
|
||||
self.__class__.__name__,
|
||||
self.id,
|
||||
self.type,
|
||||
self.default,
|
||||
self.options,
|
||||
)
|
||||
|
||||
|
||||
class ConfigurableManager(object):
|
||||
"""
|
||||
Provides convenience methods for storing and retrieving configuration options for nodes.
|
||||
"""
|
||||
|
||||
_default_node = -1
|
||||
_default_type = _default_node
|
||||
|
||||
|
@ -187,11 +197,15 @@ class ConfigurableManager(object):
|
|||
:param str config_type: configuration type to store configuration for
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("setting config for node(%s) type(%s): %s", node_id, config_type, config)
|
||||
logging.debug(
|
||||
"setting config for node(%s) type(%s): %s", node_id, config_type, config
|
||||
)
|
||||
node_configs = self.node_configurations.setdefault(node_id, OrderedDict())
|
||||
node_configs[config_type] = config
|
||||
|
||||
def get_config(self, _id, node_id=_default_node, config_type=_default_type, default=None):
|
||||
def get_config(
|
||||
self, _id, node_id=_default_node, config_type=_default_type, default=None
|
||||
):
|
||||
"""
|
||||
Retrieves a specific configuration for a node and configuration type.
|
||||
|
||||
|
@ -256,6 +270,7 @@ class ConfigurableOptions(object):
|
|||
"""
|
||||
Provides a base for defining configuration options within CORE.
|
||||
"""
|
||||
|
||||
name = None
|
||||
bitmap = None
|
||||
options = []
|
||||
|
@ -278,9 +293,7 @@ class ConfigurableOptions(object):
|
|||
:return: configuration group definition
|
||||
:rtype: list[ConfigGroup]
|
||||
"""
|
||||
return [
|
||||
ConfigGroup("Options", 1, len(cls.configurations()))
|
||||
]
|
||||
return [ConfigGroup("Options", 1, len(cls.configurations()))]
|
||||
|
||||
@classmethod
|
||||
def default_values(cls):
|
||||
|
@ -290,7 +303,9 @@ class ConfigurableOptions(object):
|
|||
:return: ordered configuration mapping default values
|
||||
:rtype: OrderedDict
|
||||
"""
|
||||
return OrderedDict([(config.id, config.default) for config in cls.configurations()])
|
||||
return OrderedDict(
|
||||
[(config.id, config.default) for config in cls.configurations()]
|
||||
)
|
||||
|
||||
|
||||
class ModelManager(ConfigurableManager):
|
||||
|
@ -365,7 +380,9 @@ class ModelManager(ConfigurableManager):
|
|||
:param dict config: model configuration, None for default configuration
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("setting mobility model(%s) for node(%s): %s", model_class.name, node.id, config)
|
||||
logging.debug(
|
||||
"setting model(%s) for node(%s): %s", model_class.name, node.id, config
|
||||
)
|
||||
self.set_model_config(node.id, model_class.name, config)
|
||||
config = self.get_model_config(node.id, model_class.name)
|
||||
node.setmodel(model_class, config)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
"""
|
||||
EMANE Bypass model for CORE
|
||||
"""
|
||||
from core.config import ConfigGroup
|
||||
from core.config import Configuration
|
||||
from core.config import ConfigGroup, Configuration
|
||||
from core.emane import emanemodel
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
|
||||
|
@ -21,7 +20,7 @@ class EmaneBypassModel(emanemodel.EmaneModel):
|
|||
_type=ConfigDataTypes.BOOL,
|
||||
default="0",
|
||||
options=["True", "False"],
|
||||
label="There are no parameters for the bypass model."
|
||||
label="There are no parameters for the bypass model.",
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -37,6 +36,4 @@ class EmaneBypassModel(emanemodel.EmaneModel):
|
|||
# override config groups
|
||||
@classmethod
|
||||
def config_groups(cls):
|
||||
return [
|
||||
ConfigGroup("Bypass Parameters", 1, 1),
|
||||
]
|
||||
return [ConfigGroup("Bypass Parameters", 1, 1)]
|
||||
|
|
|
@ -4,12 +4,13 @@ commeffect.py: EMANE CommEffect model for CORE
|
|||
|
||||
import logging
|
||||
import os
|
||||
from builtins import int
|
||||
|
||||
from lxml import etree
|
||||
from past.builtins import basestring
|
||||
|
||||
from core.config import ConfigGroup
|
||||
from core.emane import emanemanifest
|
||||
from core.emane import emanemodel
|
||||
from core.emane import emanemanifest, emanemodel
|
||||
from core.xml import emanexml
|
||||
|
||||
try:
|
||||
|
@ -56,9 +57,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
|
||||
@classmethod
|
||||
def config_groups(cls):
|
||||
return [
|
||||
ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))
|
||||
]
|
||||
return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))]
|
||||
|
||||
def build_xml_files(self, config, interface=None):
|
||||
"""
|
||||
|
@ -76,7 +75,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
shim_name = emanexml.shim_file_name(self, interface)
|
||||
|
||||
# create and write nem document
|
||||
nem_element = etree.Element("nem", name="%s NEM" % self.name, type="unstructured")
|
||||
nem_element = etree.Element(
|
||||
"nem", name="%s NEM" % self.name, type="unstructured"
|
||||
)
|
||||
transport_type = "virtual"
|
||||
if interface and interface.transport_type == "raw":
|
||||
transport_type = "raw"
|
||||
|
@ -90,7 +91,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
emanexml.create_file(nem_element, "nem", nem_file)
|
||||
|
||||
# create and write shim document
|
||||
shim_element = etree.Element("shim", name="%s SHIM" % self.name, library=self.shim_library)
|
||||
shim_element = etree.Element(
|
||||
"shim", name="%s SHIM" % self.name, library=self.shim_library
|
||||
)
|
||||
|
||||
# append all shim options (except filterfile) to shimdoc
|
||||
for configuration in self.config_shim:
|
||||
|
@ -108,7 +111,16 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
shim_file = os.path.join(self.session.session_dir, shim_name)
|
||||
emanexml.create_file(shim_element, "shim", shim_file)
|
||||
|
||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
):
|
||||
"""
|
||||
Generate CommEffect events when a Link Message is received having
|
||||
link parameters.
|
||||
|
@ -136,7 +148,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
jitter=convert_none(jitter),
|
||||
loss=convert_none(loss),
|
||||
duplicate=convert_none(duplicate),
|
||||
unicast=long(convert_none(bw)),
|
||||
broadcast=long(convert_none(mbw))
|
||||
unicast=int(convert_none(bw)),
|
||||
broadcast=int(convert_none(mbw)),
|
||||
)
|
||||
service.publish(nemid2, event)
|
||||
|
|
|
@ -7,13 +7,9 @@ import logging
|
|||
import os
|
||||
import threading
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.api.tlv import coreapi, dataconversion
|
||||
from core.config import ConfigGroup
|
||||
from core.config import ConfigShim
|
||||
from core.config import Configuration
|
||||
from core.config import ModelManager
|
||||
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
|
||||
from core.emane import emanemanifest
|
||||
from core.emane.bypass import EmaneBypassModel
|
||||
from core.emane.commeffect import EmaneCommEffectModel
|
||||
|
@ -21,13 +17,15 @@ from core.emane.emanemodel import EmaneModel
|
|||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emane.tdma import EmaneTdmaModel
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.emulator.enumerations import ConfigFlags
|
||||
from core.emulator.enumerations import ConfigTlvs
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.emulator.enumerations import (
|
||||
ConfigDataTypes,
|
||||
ConfigFlags,
|
||||
ConfigTlvs,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTypes,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.nodes import nodeutils
|
||||
from core.xml import emanexml
|
||||
|
||||
|
@ -48,7 +46,7 @@ EMANE_MODELS = [
|
|||
EmaneIeee80211abgModel,
|
||||
EmaneCommEffectModel,
|
||||
EmaneBypassModel,
|
||||
EmaneTdmaModel
|
||||
EmaneTdmaModel,
|
||||
]
|
||||
DEFAULT_EMANE_PREFIX = "/usr"
|
||||
|
||||
|
@ -59,6 +57,7 @@ class EmaneManager(ModelManager):
|
|||
building EMANE config files from all of the EmaneNode objects in this
|
||||
emulation, and for controlling the EMANE daemons.
|
||||
"""
|
||||
|
||||
name = "emane"
|
||||
config_type = RegisterTlvs.EMULATION_SERVER.value
|
||||
SUCCESS, NOT_NEEDED, NOT_READY = (0, 1, 2)
|
||||
|
@ -79,8 +78,12 @@ class EmaneManager(ModelManager):
|
|||
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)
|
||||
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
|
||||
self.platformport = self.session.options.get_config_int(
|
||||
"emane_platform_port", 8100
|
||||
)
|
||||
self.transformport = self.session.options.get_config_int(
|
||||
"emane_transform_port", 8200
|
||||
)
|
||||
self.doeventloop = False
|
||||
self.eventmonthread = None
|
||||
|
||||
|
@ -124,7 +127,9 @@ class EmaneManager(ModelManager):
|
|||
|
||||
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
||||
if not config:
|
||||
config = self.get_configs(node_id=interface.node.id, config_type=model_name)
|
||||
config = self.get_configs(
|
||||
node_id=interface.node.id, config_type=model_name
|
||||
)
|
||||
|
||||
# get non interface config, when none found
|
||||
if not config:
|
||||
|
@ -186,11 +191,15 @@ class EmaneManager(ModelManager):
|
|||
self.event_device = self.get_config("eventservicedevice")
|
||||
eventnetidx = self.session.get_control_net_index(self.event_device)
|
||||
if eventnetidx < 0:
|
||||
logging.error("invalid emane event service device provided: %s", self.event_device)
|
||||
logging.error(
|
||||
"invalid emane event service device provided: %s", self.event_device
|
||||
)
|
||||
return False
|
||||
|
||||
# make sure the event control network is in place
|
||||
eventnet = self.session.add_remove_control_net(net_index=eventnetidx, remove=False, conf_required=False)
|
||||
eventnet = self.session.add_remove_control_net(
|
||||
net_index=eventnetidx, remove=False, conf_required=False
|
||||
)
|
||||
if eventnet is not None:
|
||||
# direct EMANE events towards control net bridge
|
||||
self.event_device = eventnet.brname
|
||||
|
@ -211,8 +220,10 @@ class EmaneManager(ModelManager):
|
|||
Load EMANE models and make them available.
|
||||
"""
|
||||
for emane_model in emane_models:
|
||||
logging.info("loading emane model: %s", emane_model.__name__)
|
||||
emane_prefix = self.session.options.get_config("emane_prefix", default=DEFAULT_EMANE_PREFIX)
|
||||
logging.debug("loading emane model: %s", emane_model.__name__)
|
||||
emane_prefix = self.session.options.get_config(
|
||||
"emane_prefix", default=DEFAULT_EMANE_PREFIX
|
||||
)
|
||||
emane_model.load(emane_prefix)
|
||||
self.models[emane_model.name] = emane_model
|
||||
|
||||
|
@ -225,7 +236,9 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
with self._emane_node_lock:
|
||||
if emane_node.id in self._emane_nodes:
|
||||
raise KeyError("non-unique EMANE object id %s for %s" % (emane_node.id, emane_node))
|
||||
raise KeyError(
|
||||
"non-unique EMANE object id %s for %s" % (emane_node.id, emane_node)
|
||||
)
|
||||
self._emane_nodes[emane_node.id] = emane_node
|
||||
|
||||
def getnodes(self):
|
||||
|
@ -254,7 +267,9 @@ class EmaneManager(ModelManager):
|
|||
for node_id in self.session.nodes:
|
||||
node = self.session.nodes[node_id]
|
||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
||||
logging.debug("adding emane node: id(%s) name(%s)", node.id, node.name)
|
||||
logging.debug(
|
||||
"adding emane node: id(%s) name(%s)", node.id, node.name
|
||||
)
|
||||
self.add_node(node)
|
||||
|
||||
if not self._emane_nodes:
|
||||
|
@ -267,12 +282,19 @@ class EmaneManager(ModelManager):
|
|||
if self.session.master:
|
||||
otadev = self.get_config("otamanagerdevice")
|
||||
netidx = self.session.get_control_net_index(otadev)
|
||||
logging.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
||||
logging.debug(
|
||||
"emane ota manager device: index(%s) otadev(%s)", netidx, otadev
|
||||
)
|
||||
if netidx < 0:
|
||||
logging.error("EMANE cannot start, check core config. invalid OTA device provided: %s", otadev)
|
||||
logging.error(
|
||||
"EMANE cannot start, check core config. invalid OTA device provided: %s",
|
||||
otadev,
|
||||
)
|
||||
return EmaneManager.NOT_READY
|
||||
|
||||
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
||||
ctrlnet = 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)
|
||||
|
@ -280,10 +302,15 @@ class EmaneManager(ModelManager):
|
|||
netidx = self.session.get_control_net_index(eventdev)
|
||||
logging.debug("emane event service device index: %s", netidx)
|
||||
if netidx < 0:
|
||||
logging.error("EMANE cannot start, check core config. invalid event service device: %s", eventdev)
|
||||
logging.error(
|
||||
"EMANE cannot start, check core config. invalid event service device: %s",
|
||||
eventdev,
|
||||
)
|
||||
return EmaneManager.NOT_READY
|
||||
|
||||
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
||||
ctrlnet = self.session.add_remove_control_net(
|
||||
net_index=netidx, remove=False, conf_required=False
|
||||
)
|
||||
self.distributedctrlnet(ctrlnet)
|
||||
|
||||
if self.checkdistributed():
|
||||
|
@ -323,7 +350,9 @@ class EmaneManager(ModelManager):
|
|||
for node_id in self._emane_nodes:
|
||||
emane_node = self._emane_nodes[node_id]
|
||||
for netif in emane_node.netifs():
|
||||
nems.append((netif.node.name, netif.name, emane_node.getnemid(netif)))
|
||||
nems.append(
|
||||
(netif.node.name, netif.name, emane_node.getnemid(netif))
|
||||
)
|
||||
|
||||
if nems:
|
||||
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems")
|
||||
|
@ -346,7 +375,11 @@ class EmaneManager(ModelManager):
|
|||
with self._emane_node_lock:
|
||||
for key in sorted(self._emane_nodes.keys()):
|
||||
emane_node = self._emane_nodes[key]
|
||||
logging.debug("post startup for emane node: %s - %s", emane_node.id, emane_node.name)
|
||||
logging.debug(
|
||||
"post startup for emane node: %s - %s",
|
||||
emane_node.id,
|
||||
emane_node.name,
|
||||
)
|
||||
emane_node.model.post_startup()
|
||||
for netif in emane_node.netifs():
|
||||
x, y, z = netif.node.position.get()
|
||||
|
@ -361,8 +394,12 @@ class EmaneManager(ModelManager):
|
|||
self._emane_nodes.clear()
|
||||
|
||||
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
||||
self.platformport = self.session.options.get_config_int("emane_platform_port", 8100)
|
||||
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
|
||||
self.platformport = self.session.options.get_config_int(
|
||||
"emane_platform_port", 8100
|
||||
)
|
||||
self.transformport = self.session.options.get_config_int(
|
||||
"emane_transform_port", 8200
|
||||
)
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
|
@ -385,7 +422,10 @@ class EmaneManager(ModelManager):
|
|||
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:
|
||||
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:
|
||||
|
@ -450,7 +490,9 @@ class EmaneManager(ModelManager):
|
|||
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)
|
||||
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
|
||||
|
@ -469,7 +511,9 @@ class EmaneManager(ModelManager):
|
|||
# assume self._objslock is already held here
|
||||
logging.info("emane building xml...")
|
||||
# on master, control network bridge added earlier in startup()
|
||||
ctrlnet = self.session.add_remove_control_net(net_index=0, remove=False, conf_required=False)
|
||||
ctrlnet = self.session.add_remove_control_net(
|
||||
net_index=0, remove=False, conf_required=False
|
||||
)
|
||||
self.buildplatformxml(ctrlnet)
|
||||
self.buildnemxml()
|
||||
self.buildeventservicexml()
|
||||
|
@ -495,7 +539,10 @@ class EmaneManager(ModelManager):
|
|||
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))
|
||||
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")
|
||||
|
@ -510,8 +557,10 @@ class EmaneManager(ModelManager):
|
|||
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:])
|
||||
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)
|
||||
|
||||
|
@ -526,7 +575,11 @@ class EmaneManager(ModelManager):
|
|||
|
||||
# skip nodes that already have a model set
|
||||
if emane_node.model:
|
||||
logging.debug("node(%s) already has model(%s)", emane_node.id, emane_node.model.name)
|
||||
logging.debug(
|
||||
"node(%s) already has model(%s)",
|
||||
emane_node.id,
|
||||
emane_node.model.name,
|
||||
)
|
||||
continue
|
||||
|
||||
# set model configured for node, due to legacy messaging configuration before nodes exist
|
||||
|
@ -578,7 +631,9 @@ class EmaneManager(ModelManager):
|
|||
# assume self._objslock is already held here
|
||||
for key in sorted(self._emane_nodes.keys()):
|
||||
emane_node = self._emane_nodes[key]
|
||||
nemid = emanexml.build_node_platform_xml(self, ctrlnet, emane_node, nemid, platform_xmls)
|
||||
nemid = emanexml.build_node_platform_xml(
|
||||
self, ctrlnet, emane_node, nemid, platform_xmls
|
||||
)
|
||||
|
||||
def buildnemxml(self):
|
||||
"""
|
||||
|
@ -593,7 +648,9 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
Calls emanegentransportxml using a platform.xml file to build the transportdaemon*.xml.
|
||||
"""
|
||||
utils.check_cmd(["emanegentransportxml", "platform.xml"], cwd=self.session.session_dir)
|
||||
utils.check_cmd(
|
||||
["emanegentransportxml", "platform.xml"], cwd=self.session.session_dir
|
||||
)
|
||||
|
||||
def buildeventservicexml(self):
|
||||
"""
|
||||
|
@ -638,7 +695,7 @@ class EmaneManager(ModelManager):
|
|||
|
||||
emanecmd = ["emane", "-d", "-l", loglevel]
|
||||
if realtime:
|
||||
emanecmd += "-r",
|
||||
emanecmd += ("-r",)
|
||||
|
||||
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
|
||||
otadev = self.get_config("otamanagerdevice")
|
||||
|
@ -657,15 +714,21 @@ class EmaneManager(ModelManager):
|
|||
n = node.id
|
||||
|
||||
# control network not yet started here
|
||||
self.session.add_remove_control_interface(node, 0, remove=False, conf_required=False)
|
||||
self.session.add_remove_control_interface(
|
||||
node, 0, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
if otanetidx > 0:
|
||||
logging.info("adding ota device ctrl%d", otanetidx)
|
||||
self.session.add_remove_control_interface(node, otanetidx, remove=False, conf_required=False)
|
||||
self.session.add_remove_control_interface(
|
||||
node, otanetidx, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
if eventservicenetidx >= 0:
|
||||
logging.info("adding event service device ctrl%d", eventservicenetidx)
|
||||
self.session.add_remove_control_interface(node, eventservicenetidx, remove=False, conf_required=False)
|
||||
self.session.add_remove_control_interface(
|
||||
node, eventservicenetidx, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
# multicast route is needed for OTA data
|
||||
args = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
|
||||
|
@ -677,7 +740,11 @@ class EmaneManager(ModelManager):
|
|||
node.network_cmd(args)
|
||||
|
||||
# start emane
|
||||
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)]
|
||||
args = emanecmd + [
|
||||
"-f",
|
||||
os.path.join(path, "emane%d.log" % n),
|
||||
os.path.join(path, "platform%d.xml" % n),
|
||||
]
|
||||
output = node.check_cmd(args)
|
||||
logging.info("node(%s) emane daemon running: %s", node.name, args)
|
||||
logging.info("node(%s) emane daemon output: %s", node.name, output)
|
||||
|
@ -760,10 +827,12 @@ class EmaneManager(ModelManager):
|
|||
return
|
||||
|
||||
if self.service is None:
|
||||
logging.error("Warning: EMANE events will not be generated "
|
||||
"because the emaneeventservice\n binding was "
|
||||
"unable to load "
|
||||
"(install the python-emaneeventservice bindings)")
|
||||
logging.error(
|
||||
"Warning: EMANE events will not be generated "
|
||||
"because the emaneeventservice\n binding was "
|
||||
"unable to load "
|
||||
"(install the python-emaneeventservice bindings)"
|
||||
)
|
||||
return
|
||||
self.doeventloop = True
|
||||
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
|
||||
|
@ -792,7 +861,10 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
if self.service is None:
|
||||
return
|
||||
logging.info("subscribing to EMANE location events. (%s)", threading.currentThread().getName())
|
||||
logging.info(
|
||||
"subscribing to EMANE location events. (%s)",
|
||||
threading.currentThread().getName(),
|
||||
)
|
||||
while self.doeventloop is True:
|
||||
_uuid, _seq, events = self.service.nextEvent()
|
||||
|
||||
|
@ -805,7 +877,10 @@ class EmaneManager(ModelManager):
|
|||
if eid == LocationEvent.IDENTIFIER:
|
||||
self.handlelocationevent(nem, eid, data)
|
||||
|
||||
logging.info("unsubscribing from EMANE location events. (%s)", threading.currentThread().getName())
|
||||
logging.info(
|
||||
"unsubscribing from EMANE location events. (%s)",
|
||||
threading.currentThread().getName(),
|
||||
)
|
||||
|
||||
def handlelocationevent(self, rxnemid, eid, data):
|
||||
"""
|
||||
|
@ -815,7 +890,11 @@ class EmaneManager(ModelManager):
|
|||
events.restore(data)
|
||||
for event in events:
|
||||
txnemid, attrs = event
|
||||
if "latitude" not in attrs or "longitude" not in attrs or "altitude" not in attrs:
|
||||
if (
|
||||
"latitude" not in attrs
|
||||
or "longitude" not in attrs
|
||||
or "altitude" not in attrs
|
||||
):
|
||||
logging.warning("dropped invalid location event")
|
||||
continue
|
||||
|
||||
|
@ -844,20 +923,37 @@ class EmaneManager(ModelManager):
|
|||
x = int(x)
|
||||
y = int(y)
|
||||
z = int(z)
|
||||
logging.info("location event NEM %s (%s, %s, %s) -> (%s, %s, %s)", nemid, lat, lon, alt, x, y, z)
|
||||
logging.info(
|
||||
"location event NEM %s (%s, %s, %s) -> (%s, %s, %s)",
|
||||
nemid,
|
||||
lat,
|
||||
lon,
|
||||
alt,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
)
|
||||
xbit_check = x.bit_length() > 16 or x < 0
|
||||
ybit_check = y.bit_length() > 16 or y < 0
|
||||
zbit_check = z.bit_length() > 16 or z < 0
|
||||
if any([xbit_check, ybit_check, zbit_check]):
|
||||
logging.error("Unable to build node location message, received lat/long/alt exceeds coordinate "
|
||||
"space: NEM %s (%d, %d, %d)", nemid, x, y, z)
|
||||
logging.error(
|
||||
"Unable to build node location message, received lat/long/alt exceeds coordinate "
|
||||
"space: NEM %s (%d, %d, %d)",
|
||||
nemid,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
)
|
||||
return False
|
||||
|
||||
# generate a node message for this location update
|
||||
try:
|
||||
node = self.session.get_node(n)
|
||||
except KeyError:
|
||||
logging.exception("location event NEM %s has no corresponding node %s" % (nemid, n))
|
||||
logging.exception(
|
||||
"location event NEM %s has no corresponding node %s" % (nemid, n)
|
||||
)
|
||||
return False
|
||||
|
||||
# don"t use node.setposition(x,y,z) which generates an event
|
||||
|
@ -889,18 +985,26 @@ class EmaneGlobalModel(EmaneModel):
|
|||
"eventservicedevice": _DEFAULT_DEV,
|
||||
"eventservicegroup": "224.1.2.8:45703",
|
||||
"otamanagerdevice": _DEFAULT_DEV,
|
||||
"otamanagergroup": "224.1.2.8:45702"
|
||||
"otamanagergroup": "224.1.2.8:45702",
|
||||
}
|
||||
emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
|
||||
emulator_config.insert(
|
||||
0,
|
||||
Configuration(_id="platform_id_start", _type=ConfigDataTypes.INT32, default="1",
|
||||
label="Starting Platform ID (core)")
|
||||
Configuration(
|
||||
_id="platform_id_start",
|
||||
_type=ConfigDataTypes.INT32,
|
||||
default="1",
|
||||
label="Starting Platform ID (core)",
|
||||
),
|
||||
)
|
||||
|
||||
nem_config = [
|
||||
Configuration(_id="nem_id_start", _type=ConfigDataTypes.INT32, default="1",
|
||||
label="Starting NEM ID (core)")
|
||||
Configuration(
|
||||
_id="nem_id_start",
|
||||
_type=ConfigDataTypes.INT32,
|
||||
default="1",
|
||||
label="Starting NEM ID (core)",
|
||||
)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
@ -913,7 +1017,7 @@ class EmaneGlobalModel(EmaneModel):
|
|||
config_len = len(cls.configurations())
|
||||
return [
|
||||
ConfigGroup("Platform Attributes", 1, emulator_len),
|
||||
ConfigGroup("NEM Parameters", emulator_len + 1, config_len)
|
||||
ConfigGroup("NEM Parameters", emulator_len + 1, config_len),
|
||||
]
|
||||
|
||||
def __init__(self, session, _id=None):
|
||||
|
|
|
@ -122,7 +122,7 @@ def parse(manifest_path, defaults):
|
|||
_type=config_type_value,
|
||||
default=config_default,
|
||||
options=possible,
|
||||
label=config_descriptions
|
||||
label=config_descriptions,
|
||||
)
|
||||
configurations.append(configuration)
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ Defines Emane Models used within CORE.
|
|||
import logging
|
||||
import os
|
||||
|
||||
from core.config import ConfigGroup
|
||||
from core.config import Configuration
|
||||
from core.config import ConfigGroup, Configuration
|
||||
from core.emane import emanemanifest
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.location.mobility import WirelessModel
|
||||
|
@ -18,6 +17,7 @@ class EmaneModel(WirelessModel):
|
|||
handling configuration messages based on the list of
|
||||
configurable parameters. Helper functions also live here.
|
||||
"""
|
||||
|
||||
# default mac configuration settings
|
||||
mac_library = None
|
||||
mac_xml = None
|
||||
|
@ -27,18 +27,18 @@ class EmaneModel(WirelessModel):
|
|||
# default phy configuration settings, using the universal model
|
||||
phy_library = None
|
||||
phy_xml = "emanephy.xml"
|
||||
phy_defaults = {
|
||||
"subid": "1",
|
||||
"propagationmodel": "2ray",
|
||||
"noisemode": "none"
|
||||
}
|
||||
phy_defaults = {"subid": "1", "propagationmodel": "2ray", "noisemode": "none"}
|
||||
phy_config = []
|
||||
|
||||
# support for external configurations
|
||||
external_config = [
|
||||
Configuration("external", ConfigDataTypes.BOOL, default="0"),
|
||||
Configuration("platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"),
|
||||
Configuration("transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002")
|
||||
Configuration(
|
||||
"platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"
|
||||
),
|
||||
Configuration(
|
||||
"transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002"
|
||||
),
|
||||
]
|
||||
|
||||
config_ignore = set()
|
||||
|
@ -85,7 +85,7 @@ class EmaneModel(WirelessModel):
|
|||
return [
|
||||
ConfigGroup("MAC Parameters", 1, mac_len),
|
||||
ConfigGroup("PHY Parameters", mac_len + 1, phy_len),
|
||||
ConfigGroup("External Parameters", phy_len + 1, config_len)
|
||||
ConfigGroup("External Parameters", phy_len + 1, config_len),
|
||||
]
|
||||
|
||||
def build_xml_files(self, config, interface=None):
|
||||
|
@ -109,7 +109,9 @@ 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)
|
||||
emanexml.create_nem_xml(
|
||||
self, config, nem_file, transport_name, mac_name, phy_name
|
||||
)
|
||||
|
||||
# create mac xml file
|
||||
mac_file = os.path.join(self.session.session_dir, mac_name)
|
||||
|
@ -143,7 +145,16 @@ class EmaneModel(WirelessModel):
|
|||
except KeyError:
|
||||
logging.exception("error during update")
|
||||
|
||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
):
|
||||
"""
|
||||
Invoked when a Link Message is received. Default is unimplemented.
|
||||
|
||||
|
@ -156,4 +167,6 @@ class EmaneModel(WirelessModel):
|
|||
:param core.netns.vif.Veth netif2: interface two
|
||||
:return: nothing
|
||||
"""
|
||||
logging.warning("emane model(%s) does not support link configuration", self.name)
|
||||
logging.warning(
|
||||
"emane model(%s) does not support link configuration", self.name
|
||||
)
|
||||
|
|
|
@ -17,7 +17,6 @@ class EmaneIeee80211abgModel(emanemodel.EmaneModel):
|
|||
@classmethod
|
||||
def load(cls, emane_prefix):
|
||||
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
||||
emane_prefix,
|
||||
"share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
|
||||
emane_prefix, "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
|
||||
)
|
||||
super(EmaneIeee80211abgModel, cls).load(emane_prefix)
|
||||
|
|
|
@ -6,10 +6,8 @@ share the same MAC+PHY model.
|
|||
|
||||
import logging
|
||||
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
|
||||
try:
|
||||
from emane.events import LocationEvent
|
||||
|
@ -24,6 +22,7 @@ class EmaneNet(CoreNetworkBase):
|
|||
"""
|
||||
EMANE network base class.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.EMANE.value
|
||||
linktype = LinkTypes.WIRELESS.value
|
||||
# icon used
|
||||
|
@ -45,14 +44,30 @@ class EmaneNode(EmaneNet):
|
|||
self.model = None
|
||||
self.mobility = None
|
||||
|
||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
):
|
||||
"""
|
||||
The CommEffect model supports link configuration.
|
||||
"""
|
||||
if not self.model:
|
||||
return
|
||||
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
|
||||
duplicate=duplicate, jitter=jitter, netif2=netif2)
|
||||
return self.model.linkconfig(
|
||||
netif=netif,
|
||||
bw=bw,
|
||||
delay=delay,
|
||||
loss=loss,
|
||||
duplicate=duplicate,
|
||||
jitter=jitter,
|
||||
netif2=netif2,
|
||||
)
|
||||
|
||||
def config(self, conf):
|
||||
self.conf = conf
|
||||
|
@ -69,7 +84,9 @@ class EmaneNode(EmaneNet):
|
|||
def updatemodel(self, config):
|
||||
if not self.model:
|
||||
raise ValueError("no model set to update for node(%s)", self.id)
|
||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
||||
logging.info(
|
||||
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||
)
|
||||
self.model.set_configs(config, node_id=self.id)
|
||||
|
||||
def setmodel(self, model, config):
|
||||
|
@ -124,13 +141,18 @@ class EmaneNode(EmaneNet):
|
|||
EMANE daemons have been started, because that is their only chance
|
||||
to bind to the TAPs.
|
||||
"""
|
||||
if self.session.emane.genlocationevents() and self.session.emane.service is None:
|
||||
if (
|
||||
self.session.emane.genlocationevents()
|
||||
and self.session.emane.service is None
|
||||
):
|
||||
warntxt = "unable to publish EMANE events because the eventservice "
|
||||
warntxt += "Python bindings failed to load"
|
||||
logging.error(warntxt)
|
||||
|
||||
for netif in self.netifs():
|
||||
external = self.session.emane.get_config("external", self.id, self.model.name)
|
||||
external = self.session.emane.get_config(
|
||||
"external", self.id, self.model.name
|
||||
)
|
||||
if external == "0":
|
||||
netif.setaddrs()
|
||||
|
||||
|
@ -168,7 +190,17 @@ class EmaneNode(EmaneNet):
|
|||
logging.info("nemid for %s is unknown", ifname)
|
||||
return
|
||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||
logging.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)", ifname, nemid, x, y, z, lat, lon, alt)
|
||||
logging.info(
|
||||
"setnemposition %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)",
|
||||
ifname,
|
||||
nemid,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
lat,
|
||||
lon,
|
||||
alt,
|
||||
)
|
||||
event = LocationEvent()
|
||||
|
||||
# altitude must be an integer or warning is printed
|
||||
|
@ -200,8 +232,18 @@ class EmaneNode(EmaneNet):
|
|||
continue
|
||||
x, y, z = netif.node.getposition()
|
||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||
logging.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)",
|
||||
i, ifname, nemid, x, y, z, lat, lon, alt)
|
||||
logging.info(
|
||||
"setnempositions %d %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)",
|
||||
i,
|
||||
ifname,
|
||||
nemid,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
lat,
|
||||
lon,
|
||||
alt,
|
||||
)
|
||||
# altitude must be an integer or warning is printed
|
||||
alt = int(round(alt))
|
||||
event.append(nemid, latitude=lat, longitude=lon, altitude=alt)
|
||||
|
|
|
@ -17,7 +17,6 @@ class EmaneRfPipeModel(emanemodel.EmaneModel):
|
|||
@classmethod
|
||||
def load(cls, emane_prefix):
|
||||
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
||||
emane_prefix,
|
||||
"share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||
emane_prefix, "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||
)
|
||||
super(EmaneRfPipeModel, cls).load(emane_prefix)
|
||||
|
|
|
@ -21,14 +21,16 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
|||
|
||||
# add custom schedule options and ignore it when writing emane xml
|
||||
schedule_name = "schedule"
|
||||
default_schedule = os.path.join(constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml")
|
||||
default_schedule = os.path.join(
|
||||
constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml"
|
||||
)
|
||||
config_ignore = {schedule_name}
|
||||
|
||||
@classmethod
|
||||
def load(cls, emane_prefix):
|
||||
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
||||
emane_prefix,
|
||||
"share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml"
|
||||
"share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml",
|
||||
)
|
||||
super(EmaneTdmaModel, cls).load(emane_prefix)
|
||||
cls.mac_config.insert(
|
||||
|
@ -37,8 +39,8 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
|||
_id=cls.schedule_name,
|
||||
_type=ConfigDataTypes.STRING,
|
||||
default=cls.default_schedule,
|
||||
label="TDMA schedule file (core)"
|
||||
)
|
||||
label="TDMA schedule file (core)",
|
||||
),
|
||||
)
|
||||
|
||||
def post_startup(self):
|
||||
|
@ -57,5 +59,7 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
|||
event_device = self.session.emane.event_device
|
||||
|
||||
# initiate tdma schedule
|
||||
logging.info("setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device)
|
||||
logging.info(
|
||||
"setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device
|
||||
)
|
||||
utils.check_cmd(["emaneevent-tdmaschedule", "-i", event_device, schedule])
|
||||
|
|
|
@ -7,8 +7,7 @@ import sys
|
|||
import core.services
|
||||
from core.emulator.emudata import IdGen
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import nodemaps
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes import nodemaps, nodeutils
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
||||
|
||||
|
@ -73,7 +72,7 @@ class CoreEmu(object):
|
|||
service_paths = self.config.get("custom_services_dir")
|
||||
logging.debug("custom service paths: %s", service_paths)
|
||||
if service_paths:
|
||||
for service_path in service_paths.split(','):
|
||||
for service_path in service_paths.split(","):
|
||||
service_path = service_path.strip()
|
||||
custom_service_errors = ServiceManager.add_services(service_path)
|
||||
self.service_errors.extend(custom_service_errors)
|
||||
|
|
|
@ -4,117 +4,118 @@ CORE data objects.
|
|||
|
||||
import collections
|
||||
|
||||
ConfigData = collections.namedtuple("ConfigData", [
|
||||
"message_type",
|
||||
"node",
|
||||
"object",
|
||||
"type",
|
||||
"data_types",
|
||||
"data_values",
|
||||
"captions",
|
||||
"bitmap",
|
||||
"possible_values",
|
||||
"groups",
|
||||
"session",
|
||||
"interface_number",
|
||||
"network_id",
|
||||
"opaque"
|
||||
])
|
||||
ConfigData = collections.namedtuple(
|
||||
"ConfigData",
|
||||
[
|
||||
"message_type",
|
||||
"node",
|
||||
"object",
|
||||
"type",
|
||||
"data_types",
|
||||
"data_values",
|
||||
"captions",
|
||||
"bitmap",
|
||||
"possible_values",
|
||||
"groups",
|
||||
"session",
|
||||
"interface_number",
|
||||
"network_id",
|
||||
"opaque",
|
||||
],
|
||||
)
|
||||
ConfigData.__new__.__defaults__ = (None,) * len(ConfigData._fields)
|
||||
|
||||
EventData = collections.namedtuple("EventData", [
|
||||
"node",
|
||||
"event_type",
|
||||
"name",
|
||||
"data",
|
||||
"time",
|
||||
"session"
|
||||
])
|
||||
EventData = collections.namedtuple(
|
||||
"EventData", ["node", "event_type", "name", "data", "time", "session"]
|
||||
)
|
||||
EventData.__new__.__defaults__ = (None,) * len(EventData._fields)
|
||||
|
||||
ExceptionData = collections.namedtuple("ExceptionData", [
|
||||
"node",
|
||||
"session",
|
||||
"level",
|
||||
"source",
|
||||
"date",
|
||||
"text",
|
||||
"opaque"
|
||||
])
|
||||
ExceptionData = collections.namedtuple(
|
||||
"ExceptionData", ["node", "session", "level", "source", "date", "text", "opaque"]
|
||||
)
|
||||
ExceptionData.__new__.__defaults__ = (None,) * len(ExceptionData._fields)
|
||||
|
||||
FileData = collections.namedtuple("FileData", [
|
||||
"message_type",
|
||||
"node",
|
||||
"name",
|
||||
"mode",
|
||||
"number",
|
||||
"type",
|
||||
"source",
|
||||
"session",
|
||||
"data",
|
||||
"compressed_data"
|
||||
])
|
||||
FileData = collections.namedtuple(
|
||||
"FileData",
|
||||
[
|
||||
"message_type",
|
||||
"node",
|
||||
"name",
|
||||
"mode",
|
||||
"number",
|
||||
"type",
|
||||
"source",
|
||||
"session",
|
||||
"data",
|
||||
"compressed_data",
|
||||
],
|
||||
)
|
||||
FileData.__new__.__defaults__ = (None,) * len(FileData._fields)
|
||||
|
||||
NodeData = collections.namedtuple("NodeData", [
|
||||
"message_type",
|
||||
"id",
|
||||
"node_type",
|
||||
"name",
|
||||
"ip_address",
|
||||
"mac_address",
|
||||
"ip6_address",
|
||||
"model",
|
||||
"emulation_id",
|
||||
"emulation_server",
|
||||
"session",
|
||||
"x_position",
|
||||
"y_position",
|
||||
"canvas",
|
||||
"network_id",
|
||||
"services",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"altitude",
|
||||
"icon",
|
||||
"opaque"
|
||||
])
|
||||
NodeData = collections.namedtuple(
|
||||
"NodeData",
|
||||
[
|
||||
"message_type",
|
||||
"id",
|
||||
"node_type",
|
||||
"name",
|
||||
"ip_address",
|
||||
"mac_address",
|
||||
"ip6_address",
|
||||
"model",
|
||||
"emulation_id",
|
||||
"emulation_server",
|
||||
"session",
|
||||
"x_position",
|
||||
"y_position",
|
||||
"canvas",
|
||||
"network_id",
|
||||
"services",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"altitude",
|
||||
"icon",
|
||||
"opaque",
|
||||
],
|
||||
)
|
||||
NodeData.__new__.__defaults__ = (None,) * len(NodeData._fields)
|
||||
|
||||
LinkData = collections.namedtuple("LinkData", [
|
||||
"message_type",
|
||||
"node1_id",
|
||||
"node2_id",
|
||||
"delay",
|
||||
"bandwidth",
|
||||
"per",
|
||||
"dup",
|
||||
"jitter",
|
||||
"mer",
|
||||
"burst",
|
||||
"session",
|
||||
"mburst",
|
||||
"link_type",
|
||||
"gui_attributes",
|
||||
"unidirectional",
|
||||
"emulation_id",
|
||||
"network_id",
|
||||
"key",
|
||||
"interface1_id",
|
||||
"interface1_name",
|
||||
"interface1_ip4",
|
||||
"interface1_ip4_mask",
|
||||
"interface1_mac",
|
||||
"interface1_ip6",
|
||||
"interface1_ip6_mask",
|
||||
"interface2_id",
|
||||
"interface2_name",
|
||||
"interface2_ip4",
|
||||
"interface2_ip4_mask",
|
||||
"interface2_mac",
|
||||
"interface2_ip6",
|
||||
"interface2_ip6_mask",
|
||||
"opaque"
|
||||
])
|
||||
LinkData = collections.namedtuple(
|
||||
"LinkData",
|
||||
[
|
||||
"message_type",
|
||||
"node1_id",
|
||||
"node2_id",
|
||||
"delay",
|
||||
"bandwidth",
|
||||
"per",
|
||||
"dup",
|
||||
"jitter",
|
||||
"mer",
|
||||
"burst",
|
||||
"session",
|
||||
"mburst",
|
||||
"link_type",
|
||||
"gui_attributes",
|
||||
"unidirectional",
|
||||
"emulation_id",
|
||||
"network_id",
|
||||
"key",
|
||||
"interface1_id",
|
||||
"interface1_name",
|
||||
"interface1_ip4",
|
||||
"interface1_ip4_mask",
|
||||
"interface1_mac",
|
||||
"interface1_ip6",
|
||||
"interface1_ip6_mask",
|
||||
"interface2_id",
|
||||
"interface2_name",
|
||||
"interface2_ip4",
|
||||
"interface2_ip4_mask",
|
||||
"interface2_mac",
|
||||
"interface2_ip6",
|
||||
"interface2_ip6_mask",
|
||||
"opaque",
|
||||
],
|
||||
)
|
||||
LinkData.__new__.__defaults__ = (None,) * len(LinkData._fields)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.ipaddress import Ipv4Prefix
|
||||
from core.nodes.ipaddress import Ipv6Prefix
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
||||
|
||||
|
||||
class IdGen(object):
|
||||
|
@ -41,7 +38,7 @@ def create_interface(node, network, interface_data):
|
|||
addrlist=interface_data.get_addresses(),
|
||||
hwaddr=interface_data.mac,
|
||||
ifindex=interface_data.id,
|
||||
ifname=interface_data.name
|
||||
ifname=interface_data.name,
|
||||
)
|
||||
return node.netif(interface_data.id, network)
|
||||
|
||||
|
@ -64,7 +61,7 @@ def link_config(network, interface, link_options, devname=None, interface_two=No
|
|||
"loss": link_options.per,
|
||||
"duplicate": link_options.dup,
|
||||
"jitter": link_options.jitter,
|
||||
"netif2": interface_two
|
||||
"netif2": interface_two,
|
||||
}
|
||||
|
||||
# hacky check here, because physical and emane nodes do not conform to the same linkconfig interface
|
||||
|
@ -242,7 +239,7 @@ class IpPrefixes(object):
|
|||
ip4_mask=ip4_mask,
|
||||
ip6=ip6,
|
||||
ip6_mask=ip6_mask,
|
||||
mac=mac
|
||||
mac=mac,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ class MessageTypes(Enum):
|
|||
"""
|
||||
CORE message types.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
LINK = 0x02
|
||||
EXECUTE = 0x03
|
||||
|
@ -28,6 +29,7 @@ class MessageFlags(Enum):
|
|||
"""
|
||||
CORE message flags.
|
||||
"""
|
||||
|
||||
ADD = 0x01
|
||||
DELETE = 0x02
|
||||
CRI = 0x04
|
||||
|
@ -41,6 +43,7 @@ class NodeTlvs(Enum):
|
|||
"""
|
||||
Node type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NUMBER = 0x01
|
||||
TYPE = 0x02
|
||||
NAME = 0x03
|
||||
|
@ -67,6 +70,7 @@ class NodeTypes(Enum):
|
|||
"""
|
||||
Node types.
|
||||
"""
|
||||
|
||||
DEFAULT = 0
|
||||
PHYSICAL = 1
|
||||
TBD = 3
|
||||
|
@ -89,6 +93,7 @@ class Rj45Models(Enum):
|
|||
"""
|
||||
RJ45 model types.
|
||||
"""
|
||||
|
||||
LINKED = 0
|
||||
WIRELESS = 1
|
||||
INSTALLED = 2
|
||||
|
@ -99,6 +104,7 @@ class LinkTlvs(Enum):
|
|||
"""
|
||||
Link type, length, value enumerations.
|
||||
"""
|
||||
|
||||
N1_NUMBER = 0x01
|
||||
N2_NUMBER = 0x02
|
||||
DELAY = 0x03
|
||||
|
@ -137,6 +143,7 @@ class LinkTypes(Enum):
|
|||
"""
|
||||
Link types.
|
||||
"""
|
||||
|
||||
WIRELESS = 0
|
||||
WIRED = 1
|
||||
|
||||
|
@ -145,6 +152,7 @@ class ExecuteTlvs(Enum):
|
|||
"""
|
||||
Execute type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
NUMBER = 0x02
|
||||
TIME = 0x03
|
||||
|
@ -158,6 +166,7 @@ class RegisterTlvs(Enum):
|
|||
"""
|
||||
Register type, length, value enumerations.
|
||||
"""
|
||||
|
||||
WIRELESS = 0x01
|
||||
MOBILITY = 0x02
|
||||
UTILITY = 0x03
|
||||
|
@ -171,6 +180,7 @@ class ConfigTlvs(Enum):
|
|||
"""
|
||||
Configuration type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
OBJECT = 0x02
|
||||
TYPE = 0x03
|
||||
|
@ -190,6 +200,7 @@ class ConfigFlags(Enum):
|
|||
"""
|
||||
Configuration flags.
|
||||
"""
|
||||
|
||||
NONE = 0x00
|
||||
REQUEST = 0x01
|
||||
UPDATE = 0x02
|
||||
|
@ -200,6 +211,7 @@ class ConfigDataTypes(Enum):
|
|||
"""
|
||||
Configuration data types.
|
||||
"""
|
||||
|
||||
UINT8 = 0x01
|
||||
UINT16 = 0x02
|
||||
UINT32 = 0x03
|
||||
|
@ -217,6 +229,7 @@ class FileTlvs(Enum):
|
|||
"""
|
||||
File type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
NAME = 0x02
|
||||
MODE = 0x03
|
||||
|
@ -232,6 +245,7 @@ class InterfaceTlvs(Enum):
|
|||
"""
|
||||
Interface type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
NUMBER = 0x02
|
||||
NAME = 0x03
|
||||
|
@ -251,6 +265,7 @@ class EventTlvs(Enum):
|
|||
"""
|
||||
Event type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
TYPE = 0x02
|
||||
NAME = 0x03
|
||||
|
@ -263,6 +278,7 @@ class EventTypes(Enum):
|
|||
"""
|
||||
Event types.
|
||||
"""
|
||||
|
||||
NONE = 0
|
||||
DEFINITION_STATE = 1
|
||||
CONFIGURATION_STATE = 2
|
||||
|
@ -285,6 +301,7 @@ class SessionTlvs(Enum):
|
|||
"""
|
||||
Session type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NUMBER = 0x01
|
||||
NAME = 0x02
|
||||
FILE = 0x03
|
||||
|
@ -299,6 +316,7 @@ class ExceptionTlvs(Enum):
|
|||
"""
|
||||
Exception type, length, value enumerations.
|
||||
"""
|
||||
|
||||
NODE = 0x01
|
||||
SESSION = 0x02
|
||||
LEVEL = 0x03
|
||||
|
@ -312,6 +330,7 @@ class ExceptionLevels(Enum):
|
|||
"""
|
||||
Exception levels.
|
||||
"""
|
||||
|
||||
NONE = 0
|
||||
FATAL = 1
|
||||
ERROR = 2
|
||||
|
|
|
@ -15,23 +15,21 @@ import time
|
|||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
import core.nodes.base
|
||||
from core import constants
|
||||
from core import utils
|
||||
from core import CoreError, constants, utils
|
||||
from core.api.tlv import coreapi
|
||||
from core.api.tlv.broker import CoreBroker
|
||||
from core.emane.emanemanager import EmaneManager
|
||||
from core.emulator.data import EventData, NodeData
|
||||
from core.emulator.data import ExceptionData
|
||||
from core.emulator.emudata import IdGen
|
||||
from core.emulator.emudata import LinkOptions, NodeOptions
|
||||
from core.emulator.emudata import create_interface
|
||||
from core.emulator.emudata import is_net_node
|
||||
from core.emulator.emudata import link_config
|
||||
from core.emulator.enumerations import EventTypes, LinkTypes
|
||||
from core.emulator.enumerations import ExceptionLevels
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.sessionconfig import SessionConfig
|
||||
from core.emulator.sessionconfig import SessionMetaData
|
||||
from core.emulator.data import EventData, ExceptionData, NodeData
|
||||
from core.emulator.emudata import (
|
||||
IdGen,
|
||||
LinkOptions,
|
||||
NodeOptions,
|
||||
create_interface,
|
||||
is_net_node,
|
||||
link_config,
|
||||
)
|
||||
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
|
||||
from core.emulator.sessionconfig import SessionConfig, SessionMetaData
|
||||
from core.location.corelocation import CoreLocation
|
||||
from core.location.event import EventLoop
|
||||
from core.location.mobility import MobilityManager
|
||||
|
@ -40,8 +38,7 @@ from core.nodes.base import CoreNodeBase
|
|||
from core.nodes.ipaddress import MacAddress
|
||||
from core.plugins.sdt import Sdt
|
||||
from core.services.coreservices import CoreServices
|
||||
from core.xml import corexml
|
||||
from core.xml import corexmldeployment
|
||||
from core.xml import corexml, corexmldeployment
|
||||
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
|
||||
|
||||
|
||||
|
@ -85,7 +82,9 @@ class Session(object):
|
|||
# hooks handlers
|
||||
self._hooks = {}
|
||||
self._state_hooks = {}
|
||||
self.add_state_hook(state=EventTypes.RUNTIME_STATE.value, hook=self.runtime_state_hook)
|
||||
self.add_state_hook(
|
||||
state=EventTypes.RUNTIME_STATE.value, hook=self.runtime_state_hook
|
||||
)
|
||||
|
||||
# handlers for broadcasting information
|
||||
self.event_handlers = []
|
||||
|
@ -131,7 +130,9 @@ class Session(object):
|
|||
:return: nodes, network nodes if present, and tunnel if present
|
||||
:rtype: tuple
|
||||
"""
|
||||
logging.debug("link message between node1(%s) and node2(%s)", node_one_id, node_two_id)
|
||||
logging.debug(
|
||||
"link message between node1(%s) and node2(%s)", node_one_id, node_two_id
|
||||
)
|
||||
|
||||
# values to fill
|
||||
net_one = None
|
||||
|
@ -171,8 +172,14 @@ class Session(object):
|
|||
net_two = node_two
|
||||
node_two = None
|
||||
|
||||
logging.debug("link node types n1(%s) n2(%s) net1(%s) net2(%s) tunnel(%s)",
|
||||
node_one, node_two, net_one, net_two, tunnel)
|
||||
logging.debug(
|
||||
"link node types n1(%s) n2(%s) net1(%s) net2(%s) tunnel(%s)",
|
||||
node_one,
|
||||
node_two,
|
||||
net_one,
|
||||
net_two,
|
||||
tunnel,
|
||||
)
|
||||
return node_one, node_two, net_one, net_two, tunnel
|
||||
|
||||
# TODO: this doesn't appear to ever be used, EMANE or basic wireless range
|
||||
|
@ -183,27 +190,47 @@ class Session(object):
|
|||
:param list objects: possible objects to deal with
|
||||
:param bool connect: link interfaces if True, unlink otherwise
|
||||
:return: nothing
|
||||
:raises core.CoreError: when objects to link is less than 2, or no common networks are found
|
||||
"""
|
||||
objects = [x for x in objects if x]
|
||||
if len(objects) < 2:
|
||||
raise ValueError("wireless link failure: %s", objects)
|
||||
logging.debug("handling wireless linking objects(%s) connect(%s)", objects, connect)
|
||||
raise CoreError("wireless link failure: %s" % objects)
|
||||
logging.debug(
|
||||
"handling wireless linking objects(%s) connect(%s)", objects, connect
|
||||
)
|
||||
common_networks = objects[0].commonnets(objects[1])
|
||||
if not common_networks:
|
||||
raise ValueError("no common network found for wireless link/unlink")
|
||||
raise CoreError("no common network found for wireless link/unlink")
|
||||
|
||||
for common_network, interface_one, interface_two in common_networks:
|
||||
if not nodeutils.is_node(common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]):
|
||||
logging.info("skipping common network that is not wireless/emane: %s", common_network)
|
||||
if not nodeutils.is_node(
|
||||
common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]
|
||||
):
|
||||
logging.info(
|
||||
"skipping common network that is not wireless/emane: %s",
|
||||
common_network,
|
||||
)
|
||||
continue
|
||||
|
||||
logging.info("wireless linking connect(%s): %s - %s", connect, interface_one, interface_two)
|
||||
logging.info(
|
||||
"wireless linking connect(%s): %s - %s",
|
||||
connect,
|
||||
interface_one,
|
||||
interface_two,
|
||||
)
|
||||
if connect:
|
||||
common_network.link(interface_one, interface_two)
|
||||
else:
|
||||
common_network.unlink(interface_one, interface_two)
|
||||
|
||||
def add_link(self, node_one_id, node_two_id, interface_one=None, interface_two=None, link_options=None):
|
||||
def add_link(
|
||||
self,
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
interface_one=None,
|
||||
interface_two=None,
|
||||
link_options=None,
|
||||
):
|
||||
"""
|
||||
Add a link between nodes.
|
||||
|
||||
|
@ -212,13 +239,15 @@ class Session(object):
|
|||
:param core.emulator.emudata.InterfaceData interface_one: node one interface data, defaults to none
|
||||
:param core.emulator.emudata.InterfaceData interface_two: node two interface data, defaults to none
|
||||
:param core.emulator.emudata.LinkOptions link_options: data for creating link, defaults to no options
|
||||
:return:
|
||||
:return: nothing
|
||||
"""
|
||||
if not link_options:
|
||||
link_options = LinkOptions()
|
||||
|
||||
# get node objects identified by link data
|
||||
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
|
||||
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(
|
||||
node_one_id, node_two_id
|
||||
)
|
||||
|
||||
if node_one:
|
||||
node_one.lock.acquire()
|
||||
|
@ -234,27 +263,43 @@ class Session(object):
|
|||
else:
|
||||
# 2 nodes being linked, ptp network
|
||||
if all([node_one, node_two]) and not net_one:
|
||||
logging.info("adding link for peer to peer nodes: %s - %s", node_one.name, node_two.name)
|
||||
logging.info(
|
||||
"adding link for peer to peer nodes: %s - %s",
|
||||
node_one.name,
|
||||
node_two.name,
|
||||
)
|
||||
ptp_class = nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
|
||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||
net_one = self.create_node(cls=ptp_class, start=start)
|
||||
|
||||
# node to network
|
||||
if node_one and net_one:
|
||||
logging.info("adding link from node to network: %s - %s", node_one.name, net_one.name)
|
||||
logging.info(
|
||||
"adding link from node to network: %s - %s",
|
||||
node_one.name,
|
||||
net_one.name,
|
||||
)
|
||||
interface = create_interface(node_one, net_one, interface_one)
|
||||
link_config(net_one, interface, link_options)
|
||||
|
||||
# network to node
|
||||
if node_two and net_one:
|
||||
logging.info("adding link from network to node: %s - %s", node_two.name, net_one.name)
|
||||
logging.info(
|
||||
"adding link from network to node: %s - %s",
|
||||
node_two.name,
|
||||
net_one.name,
|
||||
)
|
||||
interface = create_interface(node_two, net_one, interface_two)
|
||||
if not link_options.unidirectional:
|
||||
link_config(net_one, interface, link_options)
|
||||
|
||||
# network to network
|
||||
if net_one and net_two:
|
||||
logging.info("adding link from network to network: %s - %s", net_one.name, net_two.name)
|
||||
logging.info(
|
||||
"adding link from network to network: %s - %s",
|
||||
net_one.name,
|
||||
net_two.name,
|
||||
)
|
||||
if nodeutils.is_node(net_two, NodeTypes.RJ45):
|
||||
interface = net_two.linknet(net_one)
|
||||
else:
|
||||
|
@ -264,7 +309,9 @@ class Session(object):
|
|||
|
||||
if not link_options.unidirectional:
|
||||
interface.swapparams("_params_up")
|
||||
link_config(net_two, interface, link_options, devname=interface.name)
|
||||
link_config(
|
||||
net_two, interface, link_options, devname=interface.name
|
||||
)
|
||||
interface.swapparams("_params_up")
|
||||
|
||||
# a tunnel node was found for the nodes
|
||||
|
@ -293,12 +340,16 @@ class Session(object):
|
|||
if node_one and nodeutils.is_node(node_one, NodeTypes.PHYSICAL):
|
||||
logging.info("adding link for physical node: %s", node_one.name)
|
||||
addresses = interface_one.get_addresses()
|
||||
node_one.adoptnetif(tunnel, interface_one.id, interface_one.mac, addresses)
|
||||
node_one.adoptnetif(
|
||||
tunnel, interface_one.id, interface_one.mac, addresses
|
||||
)
|
||||
link_config(node_one, tunnel, link_options)
|
||||
elif node_two and nodeutils.is_node(node_two, NodeTypes.PHYSICAL):
|
||||
logging.info("adding link for physical node: %s", node_two.name)
|
||||
addresses = interface_two.get_addresses()
|
||||
node_two.adoptnetif(tunnel, interface_two.id, interface_two.mac, addresses)
|
||||
node_two.adoptnetif(
|
||||
tunnel, interface_two.id, interface_two.mac, addresses
|
||||
)
|
||||
link_config(node_two, tunnel, link_options)
|
||||
finally:
|
||||
if node_one:
|
||||
|
@ -306,7 +357,14 @@ class Session(object):
|
|||
if node_two:
|
||||
node_two.lock.release()
|
||||
|
||||
def delete_link(self, node_one_id, node_two_id, interface_one_id, interface_two_id, link_type=LinkTypes.WIRED):
|
||||
def delete_link(
|
||||
self,
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
interface_one_id,
|
||||
interface_two_id,
|
||||
link_type=LinkTypes.WIRED,
|
||||
):
|
||||
"""
|
||||
Delete a link between nodes.
|
||||
|
||||
|
@ -316,9 +374,12 @@ class Session(object):
|
|||
:param int interface_two_id: interface id for node two
|
||||
:param core.emulator.enumerations.LinkTypes link_type: link type to delete
|
||||
:return: nothing
|
||||
:raises core.CoreError: when no common network is found for link being deleted
|
||||
"""
|
||||
# get node objects identified by link data
|
||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(node_one_id, node_two_id)
|
||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(
|
||||
node_one_id, node_two_id
|
||||
)
|
||||
|
||||
if node_one:
|
||||
node_one.lock.acquire()
|
||||
|
@ -342,18 +403,31 @@ class Session(object):
|
|||
# otherwise get interfaces between a node and network
|
||||
if not interface_one and not interface_two:
|
||||
common_networks = node_one.commonnets(node_two)
|
||||
for network, common_interface_one, common_interface_two in common_networks:
|
||||
for (
|
||||
network,
|
||||
common_interface_one,
|
||||
common_interface_two,
|
||||
) in common_networks:
|
||||
if (net_one and network == net_one) or not net_one:
|
||||
interface_one = common_interface_one
|
||||
interface_two = common_interface_two
|
||||
break
|
||||
|
||||
if all([interface_one, interface_two]) and any([interface_one.net, interface_two.net]):
|
||||
if interface_one.net != interface_two.net and all([interface_one.up, interface_two.up]):
|
||||
raise ValueError("no common network found")
|
||||
if all([interface_one, interface_two]) and any(
|
||||
[interface_one.net, interface_two.net]
|
||||
):
|
||||
if interface_one.net != interface_two.net and all(
|
||||
[interface_one.up, interface_two.up]
|
||||
):
|
||||
raise CoreError("no common network found")
|
||||
|
||||
logging.info("deleting link node(%s):interface(%s) node(%s):interface(%s)",
|
||||
node_one.name, interface_one.name, node_two.name, interface_two.name)
|
||||
logging.info(
|
||||
"deleting link node(%s):interface(%s) node(%s):interface(%s)",
|
||||
node_one.name,
|
||||
interface_one.name,
|
||||
node_two.name,
|
||||
interface_two.name,
|
||||
)
|
||||
net_one = interface_one.net
|
||||
interface_one.detachnet()
|
||||
interface_two.detachnet()
|
||||
|
@ -364,15 +438,23 @@ class Session(object):
|
|||
elif node_one and net_one:
|
||||
interface = node_one.netif(interface_one_id)
|
||||
if interface:
|
||||
logging.info("deleting link node(%s):interface(%s) node(%s)",
|
||||
node_one.name, interface.name, net_one.name)
|
||||
logging.info(
|
||||
"deleting link node(%s):interface(%s) node(%s)",
|
||||
node_one.name,
|
||||
interface.name,
|
||||
net_one.name,
|
||||
)
|
||||
interface.detachnet()
|
||||
node_one.delnetif(interface.netindex)
|
||||
elif node_two and net_one:
|
||||
interface = node_two.netif(interface_two_id)
|
||||
if interface:
|
||||
logging.info("deleting link node(%s):interface(%s) node(%s)",
|
||||
node_two.name, interface.name, net_one.name)
|
||||
logging.info(
|
||||
"deleting link node(%s):interface(%s) node(%s)",
|
||||
node_two.name,
|
||||
interface.name,
|
||||
net_one.name,
|
||||
)
|
||||
interface.detachnet()
|
||||
node_two.delnetif(interface.netindex)
|
||||
finally:
|
||||
|
@ -381,7 +463,14 @@ class Session(object):
|
|||
if node_two:
|
||||
node_two.lock.release()
|
||||
|
||||
def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None, link_options=None):
|
||||
def update_link(
|
||||
self,
|
||||
node_one_id,
|
||||
node_two_id,
|
||||
interface_one_id=None,
|
||||
interface_two_id=None,
|
||||
link_options=None,
|
||||
):
|
||||
"""
|
||||
Update link information between nodes.
|
||||
|
||||
|
@ -391,12 +480,16 @@ class Session(object):
|
|||
:param int interface_two_id: interface id for node two
|
||||
:param core.emulator.emudata.LinkOptions link_options: data to update link with
|
||||
:return: nothing
|
||||
:raises core.CoreError: when updating a wireless type link, when there is a unknown
|
||||
link between networks
|
||||
"""
|
||||
if not link_options:
|
||||
link_options = LinkOptions()
|
||||
|
||||
# get node objects identified by link data
|
||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(node_one_id, node_two_id)
|
||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(
|
||||
node_one_id, node_two_id
|
||||
)
|
||||
|
||||
if node_one:
|
||||
node_one.lock.acquire()
|
||||
|
@ -406,7 +499,7 @@ class Session(object):
|
|||
try:
|
||||
# wireless link
|
||||
if link_options.type == LinkTypes.WIRELESS.value:
|
||||
raise ValueError("cannot update wireless link")
|
||||
raise CoreError("cannot update wireless link")
|
||||
else:
|
||||
if not node_one and not node_two:
|
||||
if net_one and net_two:
|
||||
|
@ -419,11 +512,13 @@ class Session(object):
|
|||
interface = net_two.getlinknetif(net_one)
|
||||
|
||||
if not interface:
|
||||
raise ValueError("modify unknown link between nets")
|
||||
raise CoreError("modify unknown link between nets")
|
||||
|
||||
if upstream:
|
||||
interface.swapparams("_params_up")
|
||||
link_config(net_one, interface, link_options, devname=interface.name)
|
||||
link_config(
|
||||
net_one, interface, link_options, devname=interface.name
|
||||
)
|
||||
interface.swapparams("_params_up")
|
||||
else:
|
||||
link_config(net_one, interface, link_options)
|
||||
|
@ -433,10 +528,15 @@ class Session(object):
|
|||
link_config(net_two, interface, link_options)
|
||||
else:
|
||||
interface.swapparams("_params_up")
|
||||
link_config(net_two, interface, link_options, devname=interface.name)
|
||||
link_config(
|
||||
net_two,
|
||||
interface,
|
||||
link_options,
|
||||
devname=interface.name,
|
||||
)
|
||||
interface.swapparams("_params_up")
|
||||
else:
|
||||
raise ValueError("modify link for unknown nodes")
|
||||
raise CoreError("modify link for unknown nodes")
|
||||
elif not node_one:
|
||||
# node1 = layer 2node, node2 = layer3 node
|
||||
interface = node_two.netif(interface_two_id, net_one)
|
||||
|
@ -448,15 +548,28 @@ class Session(object):
|
|||
else:
|
||||
common_networks = node_one.commonnets(node_two)
|
||||
if not common_networks:
|
||||
raise ValueError("no common network found")
|
||||
raise CoreError("no common network found")
|
||||
|
||||
for net_one, interface_one, interface_two in common_networks:
|
||||
if interface_one_id is not None and interface_one_id != node_one.getifindex(interface_one):
|
||||
if (
|
||||
interface_one_id is not None
|
||||
and interface_one_id != node_one.getifindex(interface_one)
|
||||
):
|
||||
continue
|
||||
|
||||
link_config(net_one, interface_one, link_options, interface_two=interface_two)
|
||||
link_config(
|
||||
net_one,
|
||||
interface_one,
|
||||
link_options,
|
||||
interface_two=interface_two,
|
||||
)
|
||||
if not link_options.unidirectional:
|
||||
link_config(net_one, interface_two, link_options, interface_two=interface_one)
|
||||
link_config(
|
||||
net_one,
|
||||
interface_two,
|
||||
link_options,
|
||||
interface_two=interface_one,
|
||||
)
|
||||
finally:
|
||||
if node_one:
|
||||
node_one.lock.release()
|
||||
|
@ -501,9 +614,21 @@ class Session(object):
|
|||
name = "%s%s" % (node_class.__name__, _id)
|
||||
|
||||
# create node
|
||||
logging.info("creating node(%s) id(%s) name(%s) start(%s)", node_class.__name__, _id, name, start)
|
||||
logging.info(
|
||||
"creating node(%s) id(%s) name(%s) start(%s)",
|
||||
node_class.__name__,
|
||||
_id,
|
||||
name,
|
||||
start,
|
||||
)
|
||||
if _type in [NodeTypes.DOCKER, NodeTypes.LXC]:
|
||||
node = self.create_node(cls=node_class, _id=_id, name=name, start=start, image=node_options.image)
|
||||
node = self.create_node(
|
||||
cls=node_class,
|
||||
_id=_id,
|
||||
name=name,
|
||||
start=start,
|
||||
image=node_options.image,
|
||||
)
|
||||
else:
|
||||
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
|
||||
|
||||
|
@ -516,13 +641,20 @@ class Session(object):
|
|||
self.set_node_position(node, node_options)
|
||||
|
||||
# add services to default and physical nodes only
|
||||
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL, NodeTypes.DOCKER, NodeTypes.LXC]:
|
||||
if _type in [
|
||||
NodeTypes.DEFAULT,
|
||||
NodeTypes.PHYSICAL,
|
||||
NodeTypes.DOCKER,
|
||||
NodeTypes.LXC,
|
||||
]:
|
||||
node.type = node_options.model
|
||||
logging.debug("set node type: %s", node.type)
|
||||
self.services.add_services(node, node.type, node_options.services)
|
||||
|
||||
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
||||
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(node, NodeTypes.RJ45)
|
||||
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
||||
node, NodeTypes.RJ45
|
||||
)
|
||||
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
||||
self.write_nodes()
|
||||
self.add_remove_control_interface(node=node, remove=False)
|
||||
|
@ -538,25 +670,17 @@ class Session(object):
|
|||
:param core.emulator.emudata.NodeOptions node_options: data to update node with
|
||||
:return: True if node updated, False otherwise
|
||||
:rtype: bool
|
||||
:raises core.CoreError: when node to update does not exist
|
||||
"""
|
||||
result = False
|
||||
try:
|
||||
# get node to update
|
||||
node = self.get_node(node_id)
|
||||
# get node to update
|
||||
node = self.get_node(node_id)
|
||||
|
||||
# set node position and broadcast it
|
||||
self.set_node_position(node, node_options)
|
||||
# set node position and broadcast it
|
||||
self.set_node_position(node, node_options)
|
||||
|
||||
# update attributes
|
||||
node.canvas = node_options.canvas
|
||||
node.icon = node_options.icon
|
||||
|
||||
# set node as updated successfully
|
||||
result = True
|
||||
except KeyError:
|
||||
logging.error("failure to update node that does not exist: %s", node_id)
|
||||
|
||||
return result
|
||||
# update attributes
|
||||
node.canvas = node_options.canvas
|
||||
node.icon = node_options.icon
|
||||
|
||||
def set_node_position(self, node, node_options):
|
||||
"""
|
||||
|
@ -599,7 +723,7 @@ class Session(object):
|
|||
message_type=0,
|
||||
id=node.id,
|
||||
x_position=node.position.x,
|
||||
y_position=node.position.y
|
||||
y_position=node.position.y,
|
||||
)
|
||||
self.broadcast_node(node_data)
|
||||
|
||||
|
@ -618,7 +742,10 @@ class Session(object):
|
|||
|
||||
:return: True if active, False otherwise
|
||||
"""
|
||||
result = self.state in {EventTypes.RUNTIME_STATE.value, EventTypes.DATACOLLECT_STATE.value}
|
||||
result = self.state in {
|
||||
EventTypes.RUNTIME_STATE.value,
|
||||
EventTypes.DATACOLLECT_STATE.value,
|
||||
}
|
||||
logging.info("session(%s) checking if active: %s", self.id, result)
|
||||
return result
|
||||
|
||||
|
@ -726,9 +853,18 @@ class Session(object):
|
|||
if not node_options:
|
||||
node_options = NodeOptions()
|
||||
node_options.model = "mdr"
|
||||
return self.add_node(_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options)
|
||||
return self.add_node(
|
||||
_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options
|
||||
)
|
||||
|
||||
def create_emane_network(self, model, geo_reference, geo_scale=None, node_options=NodeOptions(), config=None):
|
||||
def create_emane_network(
|
||||
self,
|
||||
model,
|
||||
geo_reference,
|
||||
geo_scale=None,
|
||||
node_options=NodeOptions(),
|
||||
config=None,
|
||||
):
|
||||
"""
|
||||
Convenience method for creating an emane network.
|
||||
|
||||
|
@ -852,7 +988,11 @@ class Session(object):
|
|||
state_name = state.name
|
||||
|
||||
if self.state == state_value:
|
||||
logging.info("session(%s) is already in state: %s, skipping change", self.id, state_name)
|
||||
logging.info(
|
||||
"session(%s) is already in state: %s, skipping change",
|
||||
self.id,
|
||||
state_name,
|
||||
)
|
||||
return
|
||||
|
||||
self.state = state_value
|
||||
|
@ -913,9 +1053,11 @@ class Session(object):
|
|||
:param str data: hook data
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("setting state hook: %s - %s from %s", hook_type, file_name, source_name)
|
||||
logging.info(
|
||||
"setting state hook: %s - %s from %s", hook_type, file_name, source_name
|
||||
)
|
||||
|
||||
_hook_id, state = hook_type.split(':')[:2]
|
||||
_hook_id, state = hook_type.split(":")[:2]
|
||||
if not state.isdigit():
|
||||
logging.error("error setting hook having state '%s'", state)
|
||||
return
|
||||
|
@ -969,8 +1111,14 @@ class Session(object):
|
|||
# execute hook file
|
||||
try:
|
||||
args = ["/bin/sh", file_name]
|
||||
subprocess.check_call(args, stdout=stdout, stderr=stderr,
|
||||
close_fds=True, cwd=self.session_dir, env=self.get_environment())
|
||||
subprocess.check_call(
|
||||
args,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
close_fds=True,
|
||||
cwd=self.session_dir,
|
||||
env=self.get_environment(),
|
||||
)
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
logging.exception("error running hook: %s", file_name)
|
||||
|
||||
|
@ -984,10 +1132,15 @@ class Session(object):
|
|||
for hook in self._state_hooks.get(state, []):
|
||||
try:
|
||||
hook(state)
|
||||
except:
|
||||
message = "exception occured when running %s state hook: %s" % (coreapi.state_name(state), hook)
|
||||
except Exception:
|
||||
message = "exception occured when running %s state hook: %s" % (
|
||||
coreapi.state_name(state),
|
||||
hook,
|
||||
)
|
||||
logging.exception(message)
|
||||
self.exception(ExceptionLevels.ERROR, "Session.run_state_hooks", None, message)
|
||||
self.exception(
|
||||
ExceptionLevels.ERROR, "Session.run_state_hooks", None, message
|
||||
)
|
||||
|
||||
def add_state_hook(self, state, hook):
|
||||
"""
|
||||
|
@ -999,7 +1152,7 @@ class Session(object):
|
|||
"""
|
||||
hooks = self._state_hooks.setdefault(state, [])
|
||||
if hook in hooks:
|
||||
raise ValueError("attempting to add duplicate state hook")
|
||||
raise CoreError("attempting to add duplicate state hook")
|
||||
hooks.append(hook)
|
||||
|
||||
if self.state == state:
|
||||
|
@ -1060,15 +1213,23 @@ class Session(object):
|
|||
if os.path.isfile(environment_config_file):
|
||||
utils.load_config(environment_config_file, env)
|
||||
except IOError:
|
||||
logging.warning("environment configuration file does not exist: %s", environment_config_file)
|
||||
logging.warning(
|
||||
"environment configuration file does not exist: %s",
|
||||
environment_config_file,
|
||||
)
|
||||
|
||||
# attempt to read and add user environment file
|
||||
if self.user:
|
||||
environment_user_file = os.path.join("/home", self.user, ".core", "environment")
|
||||
environment_user_file = os.path.join(
|
||||
"/home", self.user, ".core", "environment"
|
||||
)
|
||||
try:
|
||||
utils.load_config(environment_user_file, env)
|
||||
except IOError:
|
||||
logging.debug("user core environment settings file not present: %s", environment_user_file)
|
||||
logging.debug(
|
||||
"user core environment settings file not present: %s",
|
||||
environment_user_file,
|
||||
)
|
||||
|
||||
return env
|
||||
|
||||
|
@ -1118,21 +1279,22 @@ class Session(object):
|
|||
|
||||
return node_id
|
||||
|
||||
def create_node(self, cls, *clsargs, **clskwds):
|
||||
def create_node(self, cls, *args, **kwargs):
|
||||
"""
|
||||
Create an emulation node.
|
||||
|
||||
:param class cls: node class to create
|
||||
:param list clsargs: list of arguments for the class to create
|
||||
:param dict clskwds: dictionary of arguments for the class to create
|
||||
:param list args: list of arguments for the class to create
|
||||
:param dict kwargs: dictionary of arguments for the class to create
|
||||
:return: the created node instance
|
||||
:raises core.CoreError: when id of the node to create already exists
|
||||
"""
|
||||
node = cls(self, *clsargs, **clskwds)
|
||||
node = cls(self, *args, **kwargs)
|
||||
|
||||
with self._nodes_lock:
|
||||
if node.id in self.nodes:
|
||||
node.shutdown()
|
||||
raise KeyError("duplicate node id %s for %s" % (node.id, node.name))
|
||||
raise CoreError("duplicate node id %s for %s" % (node.id, node.name))
|
||||
self.nodes[node.id] = node
|
||||
|
||||
return node
|
||||
|
@ -1144,9 +1306,10 @@ class Session(object):
|
|||
:param int _id: node id to retrieve
|
||||
:return: node for the given id
|
||||
:rtype: core.nodes.base.CoreNode
|
||||
:raises core.CoreError: when node does not exist
|
||||
"""
|
||||
if _id not in self.nodes:
|
||||
raise KeyError("unknown node id %s" % _id)
|
||||
raise CoreError("unknown node id %s" % _id)
|
||||
return self.nodes[_id]
|
||||
|
||||
def delete_node(self, _id):
|
||||
|
@ -1158,6 +1321,7 @@ class Session(object):
|
|||
:rtype: bool
|
||||
"""
|
||||
# delete node and check for session shutdown if a node was removed
|
||||
logging.info("deleting node(%s)", _id)
|
||||
result = False
|
||||
with self._nodes_lock:
|
||||
if _id in self.nodes:
|
||||
|
@ -1190,7 +1354,9 @@ class Session(object):
|
|||
with open(file_path, "w") as f:
|
||||
for _id in self.nodes.keys():
|
||||
node = self.nodes[_id]
|
||||
f.write("%s %s %s %s\n" % (_id, node.name, node.apitype, type(node)))
|
||||
f.write(
|
||||
"%s %s %s %s\n" % (_id, node.name, node.apitype, type(node))
|
||||
)
|
||||
except IOError:
|
||||
logging.exception("error writing nodes file")
|
||||
|
||||
|
@ -1199,8 +1365,13 @@ class Session(object):
|
|||
Log information about the session in its current state.
|
||||
"""
|
||||
logging.info("session id=%s name=%s state=%s", self.id, self.name, self.state)
|
||||
logging.info("file=%s thumbnail=%s node_count=%s/%s",
|
||||
self.file_name, self.thumbnail, self.get_node_count(), len(self.nodes))
|
||||
logging.info(
|
||||
"file=%s thumbnail=%s node_count=%s/%s",
|
||||
self.file_name,
|
||||
self.thumbnail,
|
||||
self.get_node_count(),
|
||||
len(self.nodes),
|
||||
)
|
||||
|
||||
def exception(self, level, source, node_id, text):
|
||||
"""
|
||||
|
@ -1219,7 +1390,7 @@ class Session(object):
|
|||
level=level,
|
||||
source=source,
|
||||
date=time.ctime(),
|
||||
text=text
|
||||
text=text,
|
||||
)
|
||||
|
||||
self.broadcast_exception(exception_data)
|
||||
|
@ -1270,8 +1441,12 @@ class Session(object):
|
|||
count = 0
|
||||
for node_id in self.nodes:
|
||||
node = self.nodes[node_id]
|
||||
is_p2p_ctrlnet = nodeutils.is_node(node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET))
|
||||
is_tap = nodeutils.is_node(node, NodeTypes.TAP_BRIDGE) and not nodeutils.is_node(node, NodeTypes.TUNNEL)
|
||||
is_p2p_ctrlnet = nodeutils.is_node(
|
||||
node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET)
|
||||
)
|
||||
is_tap = nodeutils.is_node(
|
||||
node, NodeTypes.TAP_BRIDGE
|
||||
) and not nodeutils.is_node(node, NodeTypes.TUNNEL)
|
||||
if is_p2p_ctrlnet or is_tap:
|
||||
continue
|
||||
|
||||
|
@ -1288,8 +1463,11 @@ class Session(object):
|
|||
# this is called from instantiate() after receiving an event message
|
||||
# for the instantiation state, and from the broker when distributed
|
||||
# nodes have been started
|
||||
logging.info("session(%s) checking if not in runtime state, current state: %s", self.id,
|
||||
coreapi.state_name(self.state))
|
||||
logging.debug(
|
||||
"session(%s) checking if not in runtime state, current state: %s",
|
||||
self.id,
|
||||
coreapi.state_name(self.state),
|
||||
)
|
||||
if self.state == EventTypes.RUNTIME_STATE.value:
|
||||
logging.info("valid runtime state found, returning")
|
||||
return
|
||||
|
@ -1336,7 +1514,9 @@ class Session(object):
|
|||
and links remain.
|
||||
"""
|
||||
node_count = self.get_node_count()
|
||||
logging.info("session(%s) checking shutdown: %s nodes remaining", self.id, node_count)
|
||||
logging.debug(
|
||||
"session(%s) checking shutdown: %s nodes remaining", self.id, node_count
|
||||
)
|
||||
|
||||
shutdown = False
|
||||
if node_count == 0:
|
||||
|
@ -1367,9 +1547,15 @@ class Session(object):
|
|||
for _id in self.nodes:
|
||||
node = self.nodes[_id]
|
||||
# TODO: PyCoreNode is not the type to check
|
||||
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(node, NodeTypes.RJ45):
|
||||
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
||||
node, NodeTypes.RJ45
|
||||
):
|
||||
# add a control interface if configured
|
||||
logging.info("booting node(%s): %s", node.name, node.services)
|
||||
logging.info(
|
||||
"booting node(%s): %s",
|
||||
node.name,
|
||||
[x.name for x in node.services],
|
||||
)
|
||||
self.add_remove_control_interface(node=node, remove=False)
|
||||
result = pool.apply_async(self.services.boot_services, (node,))
|
||||
results.append(result)
|
||||
|
@ -1449,7 +1635,12 @@ class Session(object):
|
|||
:return: control net node
|
||||
:rtype: core.nodes.network.CtrlNet
|
||||
"""
|
||||
logging.debug("add/remove control net: index(%s) remove(%s) conf_required(%s)", net_index, remove, conf_required)
|
||||
logging.debug(
|
||||
"add/remove control net: index(%s) remove(%s) conf_required(%s)",
|
||||
net_index,
|
||||
remove,
|
||||
conf_required,
|
||||
)
|
||||
prefix_spec_list = self.get_control_net_prefixes()
|
||||
prefix_spec = prefix_spec_list[net_index]
|
||||
if not prefix_spec:
|
||||
|
@ -1518,10 +1709,12 @@ class Session(object):
|
|||
break
|
||||
|
||||
if not prefix:
|
||||
logging.error("control network prefix not found for server: %s", servers[0])
|
||||
logging.error(
|
||||
"control network prefix not found for server: %s", servers[0]
|
||||
)
|
||||
assign_address = False
|
||||
try:
|
||||
prefix = prefixes[0].split(':', 1)[1]
|
||||
prefix = prefixes[0].split(":", 1)[1]
|
||||
except IndexError:
|
||||
prefix = prefixes[0]
|
||||
# len(prefixes) == 1
|
||||
|
@ -1533,9 +1726,14 @@ class Session(object):
|
|||
|
||||
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
||||
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
||||
control_net = self.create_node(cls=control_net_class, _id=_id, prefix=prefix,
|
||||
assign_address=assign_address,
|
||||
updown_script=updown_script, serverintf=server_interface)
|
||||
control_net = self.create_node(
|
||||
cls=control_net_class,
|
||||
_id=_id,
|
||||
prefix=prefix,
|
||||
assign_address=assign_address,
|
||||
updown_script=updown_script,
|
||||
serverintf=server_interface,
|
||||
)
|
||||
|
||||
# tunnels between controlnets will be built with Broker.addnettunnels()
|
||||
# TODO: potentially remove documentation saying node ids are ints
|
||||
|
@ -1546,7 +1744,9 @@ class Session(object):
|
|||
|
||||
return control_net
|
||||
|
||||
def add_remove_control_interface(self, node, net_index=0, remove=False, conf_required=True):
|
||||
def add_remove_control_interface(
|
||||
self, node, net_index=0, remove=False, conf_required=True
|
||||
):
|
||||
"""
|
||||
Add a control interface to a node when a 'controlnet' prefix is
|
||||
listed in the config file or session options. Uses
|
||||
|
@ -1574,7 +1774,10 @@ class Session(object):
|
|||
control_ip = node.id
|
||||
|
||||
try:
|
||||
addrlist = ["%s/%s" % (control_net.prefix.addr(control_ip), control_net.prefix.prefixlen)]
|
||||
addrlist = [
|
||||
"%s/%s"
|
||||
% (control_net.prefix.addr(control_ip), control_net.prefix.prefixlen)
|
||||
]
|
||||
except ValueError:
|
||||
msg = "Control interface not added to node %s. " % node.id
|
||||
msg += "Invalid control network prefix (%s). " % control_net.prefix
|
||||
|
@ -1582,10 +1785,13 @@ class Session(object):
|
|||
logging.exception(msg)
|
||||
return
|
||||
|
||||
interface1 = node.newnetif(net=control_net,
|
||||
ifindex=control_net.CTRLIF_IDX_BASE + net_index,
|
||||
ifname="ctrl%d" % net_index, hwaddr=MacAddress.random(),
|
||||
addrlist=addrlist)
|
||||
interface1 = node.newnetif(
|
||||
net=control_net,
|
||||
ifindex=control_net.CTRLIF_IDX_BASE + net_index,
|
||||
ifname="ctrl%d" % net_index,
|
||||
hwaddr=MacAddress.random(),
|
||||
addrlist=addrlist,
|
||||
)
|
||||
node.netif(interface1).control = True
|
||||
|
||||
def update_control_interface_hosts(self, net_index=0, remove=False):
|
||||
|
@ -1647,15 +1853,26 @@ class Session(object):
|
|||
|
||||
if current_time > 0:
|
||||
if event_time <= current_time:
|
||||
logging.warning("could not schedule past event for time %s (run time is now %s)", event_time, current_time)
|
||||
logging.warning(
|
||||
"could not schedule past event for time %s (run time is now %s)",
|
||||
event_time,
|
||||
current_time,
|
||||
)
|
||||
return
|
||||
event_time = event_time - current_time
|
||||
|
||||
self.event_loop.add_event(event_time, self.run_event, node=node, name=name, data=data)
|
||||
self.event_loop.add_event(
|
||||
event_time, self.run_event, node=node, name=name, data=data
|
||||
)
|
||||
|
||||
if not name:
|
||||
name = ""
|
||||
logging.info("scheduled event %s at time %s data=%s", name, event_time + current_time, data)
|
||||
logging.info(
|
||||
"scheduled event %s at time %s data=%s",
|
||||
name,
|
||||
event_time + current_time,
|
||||
data,
|
||||
)
|
||||
|
||||
# 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):
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
from core.config import ConfigurableManager
|
||||
from core.config import ConfigurableOptions
|
||||
from core.config import Configuration
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.config import ConfigurableManager, ConfigurableOptions, Configuration
|
||||
from core.emulator.enumerations import ConfigDataTypes, RegisterTlvs
|
||||
from core.plugins.sdt import Sdt
|
||||
|
||||
|
||||
|
@ -10,21 +7,56 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
|
|||
"""
|
||||
Provides session configuration.
|
||||
"""
|
||||
|
||||
name = "session"
|
||||
options = [
|
||||
Configuration(_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"),
|
||||
Configuration(_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"),
|
||||
Configuration(_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"),
|
||||
Configuration(_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"),
|
||||
Configuration(_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3"),
|
||||
Configuration(_id="controlnet_updown_script", _type=ConfigDataTypes.STRING, label="Control Network Script"),
|
||||
Configuration(_id="enablerj45", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"],
|
||||
label="Enable RJ45s"),
|
||||
Configuration(_id="preservedir", _type=ConfigDataTypes.BOOL, default="0", options=["On", "Off"],
|
||||
label="Preserve session dir"),
|
||||
Configuration(_id="enablesdt", _type=ConfigDataTypes.BOOL, default="0", options=["On", "Off"],
|
||||
label="Enable SDT3D output"),
|
||||
Configuration(_id="sdturl", _type=ConfigDataTypes.STRING, default=Sdt.DEFAULT_SDT_URL, label="SDT3D URL")
|
||||
Configuration(
|
||||
_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"
|
||||
),
|
||||
Configuration(
|
||||
_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"
|
||||
),
|
||||
Configuration(
|
||||
_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"
|
||||
),
|
||||
Configuration(
|
||||
_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"
|
||||
),
|
||||
Configuration(
|
||||
_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3"
|
||||
),
|
||||
Configuration(
|
||||
_id="controlnet_updown_script",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label="Control Network Script",
|
||||
),
|
||||
Configuration(
|
||||
_id="enablerj45",
|
||||
_type=ConfigDataTypes.BOOL,
|
||||
default="1",
|
||||
options=["On", "Off"],
|
||||
label="Enable RJ45s",
|
||||
),
|
||||
Configuration(
|
||||
_id="preservedir",
|
||||
_type=ConfigDataTypes.BOOL,
|
||||
default="0",
|
||||
options=["On", "Off"],
|
||||
label="Preserve session dir",
|
||||
),
|
||||
Configuration(
|
||||
_id="enablesdt",
|
||||
_type=ConfigDataTypes.BOOL,
|
||||
default="0",
|
||||
options=["On", "Off"],
|
||||
label="Enable SDT3D output",
|
||||
),
|
||||
Configuration(
|
||||
_id="sdturl",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
default=Sdt.DEFAULT_SDT_URL,
|
||||
label="SDT3D URL",
|
||||
),
|
||||
]
|
||||
config_type = RegisterTlvs.UTILITY.value
|
||||
|
||||
|
@ -32,9 +64,16 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
|
|||
super(SessionConfig, self).__init__()
|
||||
self.set_configs(self.default_values())
|
||||
|
||||
def get_config(self, _id, node_id=ConfigurableManager._default_node,
|
||||
config_type=ConfigurableManager._default_type, default=None):
|
||||
value = super(SessionConfig, self).get_config(_id, node_id, config_type, default)
|
||||
def get_config(
|
||||
self,
|
||||
_id,
|
||||
node_id=ConfigurableManager._default_node,
|
||||
config_type=ConfigurableManager._default_type,
|
||||
default=None,
|
||||
):
|
||||
value = super(SessionConfig, self).get_config(
|
||||
_id, node_id, config_type, default
|
||||
)
|
||||
if value == "":
|
||||
value = default
|
||||
return value
|
||||
|
@ -58,5 +97,6 @@ class SessionMetaData(ConfigurableManager):
|
|||
passed in from configure messages destined to the "metadata" object.
|
||||
The data is not otherwise interpreted or processed.
|
||||
"""
|
||||
|
||||
name = "metadata"
|
||||
config_type = RegisterTlvs.UTILITY.value
|
||||
|
|
|
@ -17,6 +17,7 @@ class CoreLocation(object):
|
|||
track of a latitude/longitude/altitude reference point and scale in
|
||||
order to convert between X,Y and geo coordinates.
|
||||
"""
|
||||
|
||||
name = "location"
|
||||
config_type = RegisterTlvs.UTILITY.value
|
||||
|
||||
|
@ -118,7 +119,14 @@ class CoreLocation(object):
|
|||
try:
|
||||
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
|
||||
except utm.OutOfRangeError:
|
||||
logging.exception("UTM out of range error for n=%s zone=%s xyz=(%s,%s,%s)", n, zone, x, y, z)
|
||||
logging.exception(
|
||||
"UTM out of range error for n=%s zone=%s xyz=(%s,%s,%s)",
|
||||
n,
|
||||
zone,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
)
|
||||
lat, lon = self.refgeo[:2]
|
||||
# self.info("getgeo(%s,%s,%s) e=%s n=%s zone=%s lat,lon,alt=" \
|
||||
# "%.3f,%.3f,%.3f" % (x, y, z, e, n, zone, lat, lon, alt))
|
||||
|
@ -265,9 +273,9 @@ class CoreLocation(object):
|
|||
if n < 0:
|
||||
# refpt in northern hemisphere and we crossed south of equator
|
||||
n += 10000000
|
||||
zone = (zone[0], 'M')
|
||||
zone = (zone[0], "M")
|
||||
elif n > 10000000:
|
||||
# refpt in southern hemisphere and we crossed north of equator
|
||||
n -= 10000000
|
||||
zone = (zone[0], 'N')
|
||||
zone = (zone[0], "N")
|
||||
return e, n, zone
|
||||
|
|
|
@ -5,6 +5,7 @@ event.py: event loop implementation using a heap queue and threads.
|
|||
import heapq
|
||||
import threading
|
||||
import time
|
||||
|
||||
from past.builtins import cmp
|
||||
|
||||
|
||||
|
|
|
@ -12,19 +12,17 @@ from builtins import int
|
|||
from functools import total_ordering
|
||||
|
||||
from core import utils
|
||||
from core.config import ConfigGroup
|
||||
from core.config import ConfigurableOptions
|
||||
from core.config import Configuration
|
||||
from core.config import ModelManager
|
||||
from core.emulator.data import EventData
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import NodeTlvs
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
|
||||
from core.emulator.data import EventData, LinkData
|
||||
from core.emulator.enumerations import (
|
||||
ConfigDataTypes,
|
||||
EventTypes,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTlvs,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
|
||||
|
@ -34,6 +32,7 @@ class MobilityManager(ModelManager):
|
|||
Member of session class for handling configuration data for mobility and
|
||||
range models.
|
||||
"""
|
||||
|
||||
name = "MobilityManager"
|
||||
config_type = RegisterTlvs.WIRELESS.value
|
||||
|
||||
|
@ -73,13 +72,17 @@ class MobilityManager(ModelManager):
|
|||
node_ids = self.nodes()
|
||||
|
||||
for node_id in node_ids:
|
||||
logging.info("checking mobility startup for node: %s", node_id)
|
||||
logging.info("node mobility configurations: %s", self.get_all_configs(node_id))
|
||||
logging.debug("checking mobility startup for node: %s", node_id)
|
||||
logging.debug(
|
||||
"node mobility configurations: %s", self.get_all_configs(node_id)
|
||||
)
|
||||
|
||||
try:
|
||||
node = self.session.get_node(node_id)
|
||||
except KeyError:
|
||||
logging.warning("skipping mobility configuration for unknown node: %s", node_id)
|
||||
logging.warning(
|
||||
"skipping mobility configuration for unknown node: %s", node_id
|
||||
)
|
||||
continue
|
||||
|
||||
for model_name in self.models:
|
||||
|
@ -110,11 +113,13 @@ class MobilityManager(ModelManager):
|
|||
try:
|
||||
node = self.session.get_node(node_id)
|
||||
except KeyError:
|
||||
logging.exception("Ignoring event for model '%s', unknown node '%s'", name, node_id)
|
||||
logging.exception(
|
||||
"Ignoring event for model '%s', unknown node '%s'", name, node_id
|
||||
)
|
||||
return
|
||||
|
||||
# name is e.g. "mobility:ns2script"
|
||||
models = name[9:].split(',')
|
||||
models = name[9:].split(",")
|
||||
for model in models:
|
||||
try:
|
||||
cls = self.models[model]
|
||||
|
@ -122,7 +127,10 @@ class MobilityManager(ModelManager):
|
|||
logging.warning("Ignoring event for unknown model '%s'", model)
|
||||
continue
|
||||
|
||||
if cls.config_type in [RegisterTlvs.WIRELESS.value, RegisterTlvs.MOBILITY.value]:
|
||||
if cls.config_type in [
|
||||
RegisterTlvs.WIRELESS.value,
|
||||
RegisterTlvs.MOBILITY.value,
|
||||
]:
|
||||
model = node.mobility
|
||||
else:
|
||||
continue
|
||||
|
@ -132,12 +140,23 @@ class MobilityManager(ModelManager):
|
|||
continue
|
||||
|
||||
if cls.name != model.name:
|
||||
logging.warning("Ignoring event for %s wrong model %s,%s", node.name, cls.name, model.name)
|
||||
logging.warning(
|
||||
"Ignoring event for %s wrong model %s,%s",
|
||||
node.name,
|
||||
cls.name,
|
||||
model.name,
|
||||
)
|
||||
continue
|
||||
|
||||
if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value:
|
||||
if (
|
||||
event_type == EventTypes.STOP.value
|
||||
or event_type == EventTypes.RESTART.value
|
||||
):
|
||||
model.stop(move_initial=True)
|
||||
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
|
||||
if (
|
||||
event_type == EventTypes.START.value
|
||||
or event_type == EventTypes.RESTART.value
|
||||
):
|
||||
model.start()
|
||||
if event_type == EventTypes.PAUSE.value:
|
||||
model.pause()
|
||||
|
@ -166,7 +185,7 @@ class MobilityManager(ModelManager):
|
|||
event_type=event_type,
|
||||
name="mobility:%s" % model.name,
|
||||
data=data,
|
||||
time="%s" % time.time()
|
||||
time="%s" % time.time(),
|
||||
)
|
||||
|
||||
self.session.broadcast_event(event_data)
|
||||
|
@ -200,7 +219,7 @@ class MobilityManager(ModelManager):
|
|||
node_id = node.id
|
||||
self.phys[node_id] = node
|
||||
if netnum not in self.physnets:
|
||||
self.physnets[netnum] = [node_id, ]
|
||||
self.physnets[netnum] = [node_id]
|
||||
else:
|
||||
self.physnets[netnum].append(node_id)
|
||||
|
||||
|
@ -216,14 +235,19 @@ class MobilityManager(ModelManager):
|
|||
:param message: link message to handle
|
||||
:return: nothing
|
||||
"""
|
||||
if message.message_type == MessageTypes.LINK.value and message.flags & MessageFlags.ADD.value:
|
||||
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)
|
||||
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
|
||||
|
@ -271,6 +295,7 @@ class WirelessModel(ConfigurableOptions):
|
|||
Base class used by EMANE models and the basic range model.
|
||||
Used for managing arbitrary configuration parameters.
|
||||
"""
|
||||
|
||||
config_type = RegisterTlvs.WIRELESS.value
|
||||
bitmap = None
|
||||
position_callback = None
|
||||
|
@ -323,21 +348,44 @@ class BasicRangeModel(WirelessModel):
|
|||
and unlinks nodes based on this distance. This was formerly done from
|
||||
the GUI.
|
||||
"""
|
||||
|
||||
name = "basic_range"
|
||||
options = [
|
||||
Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
|
||||
Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT64, default="54000000", label="bandwidth (bps)"),
|
||||
Configuration(_id="jitter", _type=ConfigDataTypes.UINT64, default="0", label="transmission jitter (usec)"),
|
||||
Configuration(_id="delay", _type=ConfigDataTypes.UINT64, default="5000",
|
||||
label="transmission delay (usec)"),
|
||||
Configuration(_id="error", _type=ConfigDataTypes.STRING, default="0", label="error rate (%)")
|
||||
Configuration(
|
||||
_id="range",
|
||||
_type=ConfigDataTypes.UINT32,
|
||||
default="275",
|
||||
label="wireless range (pixels)",
|
||||
),
|
||||
Configuration(
|
||||
_id="bandwidth",
|
||||
_type=ConfigDataTypes.UINT64,
|
||||
default="54000000",
|
||||
label="bandwidth (bps)",
|
||||
),
|
||||
Configuration(
|
||||
_id="jitter",
|
||||
_type=ConfigDataTypes.UINT64,
|
||||
default="0",
|
||||
label="transmission jitter (usec)",
|
||||
),
|
||||
Configuration(
|
||||
_id="delay",
|
||||
_type=ConfigDataTypes.UINT64,
|
||||
default="5000",
|
||||
label="transmission delay (usec)",
|
||||
),
|
||||
Configuration(
|
||||
_id="error",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
default="0",
|
||||
label="error rate (%)",
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def config_groups(cls):
|
||||
return [
|
||||
ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))
|
||||
]
|
||||
return [ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))]
|
||||
|
||||
def __init__(self, session, _id):
|
||||
"""
|
||||
|
@ -345,7 +393,6 @@ class BasicRangeModel(WirelessModel):
|
|||
|
||||
:param core.session.Session session: related core session
|
||||
:param int _id: object id
|
||||
:param dict config: values
|
||||
"""
|
||||
super(BasicRangeModel, self).__init__(session=session, _id=_id)
|
||||
self.session = session
|
||||
|
@ -353,7 +400,7 @@ class BasicRangeModel(WirelessModel):
|
|||
self._netifs = {}
|
||||
self._netifslock = threading.Lock()
|
||||
|
||||
self.range = None
|
||||
self.range = 0
|
||||
self.bw = None
|
||||
self.delay = None
|
||||
self.loss = None
|
||||
|
@ -367,7 +414,11 @@ class BasicRangeModel(WirelessModel):
|
|||
:return: nothing
|
||||
"""
|
||||
self.range = int(float(config["range"]))
|
||||
logging.info("basic range model configured for WLAN %d using range %d", self.wlan.id, self.range)
|
||||
logging.debug(
|
||||
"basic range model configured for WLAN %d using range %d",
|
||||
self.wlan.id,
|
||||
self.range,
|
||||
)
|
||||
self.bw = int(config["bandwidth"])
|
||||
if self.bw == 0:
|
||||
self.bw = None
|
||||
|
@ -388,8 +439,14 @@ class BasicRangeModel(WirelessModel):
|
|||
"""
|
||||
with self._netifslock:
|
||||
for netif in self._netifs:
|
||||
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay, loss=self.loss, duplicate=None,
|
||||
jitter=self.jitter)
|
||||
self.wlan.linkconfig(
|
||||
netif,
|
||||
bw=self.bw,
|
||||
delay=self.delay,
|
||||
loss=self.loss,
|
||||
duplicate=None,
|
||||
jitter=self.jitter,
|
||||
)
|
||||
|
||||
def get_position(self, netif):
|
||||
"""
|
||||
|
@ -532,7 +589,7 @@ class BasicRangeModel(WirelessModel):
|
|||
node1_id=interface1.node.id,
|
||||
node2_id=interface2.node.id,
|
||||
network_id=self.wlan.id,
|
||||
link_type=LinkTypes.WIRELESS.value
|
||||
link_type=LinkTypes.WIRELESS.value,
|
||||
)
|
||||
|
||||
def sendlinkmsg(self, netif, netif2, unlink=False):
|
||||
|
@ -606,6 +663,7 @@ class WayPointMobility(WirelessModel):
|
|||
"""
|
||||
Abstract class for mobility models that set node waypoints.
|
||||
"""
|
||||
|
||||
name = "waypoint"
|
||||
config_type = RegisterTlvs.MOBILITY.value
|
||||
|
||||
|
@ -666,7 +724,9 @@ class WayPointMobility(WirelessModel):
|
|||
# no more waypoints or queued items, loop?
|
||||
if not self.empty_queue_stop:
|
||||
# keep running every refresh_ms, even with empty queue
|
||||
self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround)
|
||||
self.session.event_loop.add_event(
|
||||
0.001 * self.refresh_ms, self.runround
|
||||
)
|
||||
return
|
||||
if not self.loopwaypoints():
|
||||
return self.stop(move_initial=False)
|
||||
|
@ -918,16 +978,50 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
Handles the ns-2 script format, generated by scengen/setdest or
|
||||
BonnMotion.
|
||||
"""
|
||||
|
||||
name = "ns2script"
|
||||
options = [
|
||||
Configuration(_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"),
|
||||
Configuration(_id="refresh_ms", _type=ConfigDataTypes.UINT32, default="50", label="refresh time (ms)"),
|
||||
Configuration(_id="loop", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"], label="loop"),
|
||||
Configuration(_id="autostart", _type=ConfigDataTypes.STRING, label="auto-start seconds (0.0 for runtime)"),
|
||||
Configuration(_id="map", _type=ConfigDataTypes.STRING, label="node mapping (optional, e.g. 0:1,1:2,2:3)"),
|
||||
Configuration(_id="script_start", _type=ConfigDataTypes.STRING, label="script file to run upon start"),
|
||||
Configuration(_id="script_pause", _type=ConfigDataTypes.STRING, label="script file to run upon pause"),
|
||||
Configuration(_id="script_stop", _type=ConfigDataTypes.STRING, label="script file to run upon stop")
|
||||
Configuration(
|
||||
_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"
|
||||
),
|
||||
Configuration(
|
||||
_id="refresh_ms",
|
||||
_type=ConfigDataTypes.UINT32,
|
||||
default="50",
|
||||
label="refresh time (ms)",
|
||||
),
|
||||
Configuration(
|
||||
_id="loop",
|
||||
_type=ConfigDataTypes.BOOL,
|
||||
default="1",
|
||||
options=["On", "Off"],
|
||||
label="loop",
|
||||
),
|
||||
Configuration(
|
||||
_id="autostart",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label="auto-start seconds (0.0 for runtime)",
|
||||
),
|
||||
Configuration(
|
||||
_id="map",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label="node mapping (optional, e.g. 0:1,1:2,2:3)",
|
||||
),
|
||||
Configuration(
|
||||
_id="script_start",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label="script file to run upon start",
|
||||
),
|
||||
Configuration(
|
||||
_id="script_pause",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label="script file to run upon pause",
|
||||
),
|
||||
Configuration(
|
||||
_id="script_stop",
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label="script file to run upon stop",
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
@ -958,7 +1052,11 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
|
||||
def update_config(self, config):
|
||||
self.file = config["file"]
|
||||
logging.info("ns-2 scripted mobility configured for WLAN %d using file: %s", self.id, self.file)
|
||||
logging.info(
|
||||
"ns-2 scripted mobility configured for WLAN %d using file: %s",
|
||||
self.id,
|
||||
self.file,
|
||||
)
|
||||
self.refresh_ms = int(config["refresh_ms"])
|
||||
self.loop = config["loop"].lower() == "on"
|
||||
self.autostart = config["autostart"]
|
||||
|
@ -982,7 +1080,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
try:
|
||||
f = open(filename, "r")
|
||||
except IOError:
|
||||
logging.exception("ns-2 scripted mobility failed to load file: %s", self.file)
|
||||
logging.exception(
|
||||
"ns-2 scripted mobility failed to load file: %s", self.file
|
||||
)
|
||||
return
|
||||
logging.info("reading ns-2 script file: %s" % filename)
|
||||
ln = 0
|
||||
|
@ -990,7 +1090,7 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
inodenum = None
|
||||
for line in f:
|
||||
ln += 1
|
||||
if line[:2] != '$n':
|
||||
if line[:2] != "$n":
|
||||
continue
|
||||
try:
|
||||
if line[:8] == "$ns_ at ":
|
||||
|
@ -1001,7 +1101,7 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
# $ns_ at 1.00 "$node_(6) setdest 500.0 178.0 25.0"
|
||||
parts = line.split()
|
||||
time = float(parts[2])
|
||||
nodenum = parts[3][1 + parts[3].index('('):parts[3].index(')')]
|
||||
nodenum = parts[3][1 + parts[3].index("(") : parts[3].index(")")]
|
||||
x = float(parts[5])
|
||||
y = float(parts[6])
|
||||
z = None
|
||||
|
@ -1012,15 +1112,15 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
# $node_(6) set X_ 780.0
|
||||
parts = line.split()
|
||||
time = 0.0
|
||||
nodenum = parts[0][1 + parts[0].index('('):parts[0].index(')')]
|
||||
if parts[2] == 'X_':
|
||||
nodenum = parts[0][1 + parts[0].index("(") : parts[0].index(")")]
|
||||
if parts[2] == "X_":
|
||||
if ix is not None and iy is not None:
|
||||
self.addinitial(self.map(inodenum), ix, iy, iz)
|
||||
ix = iy = iz = None
|
||||
ix = float(parts[3])
|
||||
elif parts[2] == 'Y_':
|
||||
elif parts[2] == "Y_":
|
||||
iy = float(parts[3])
|
||||
elif parts[2] == 'Z_':
|
||||
elif parts[2] == "Z_":
|
||||
iz = float(parts[3])
|
||||
self.addinitial(self.map(nodenum), ix, iy, iz)
|
||||
ix = iy = iz = None
|
||||
|
@ -1028,7 +1128,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
else:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
logging.exception("skipping line %d of file %s '%s'", ln, self.file, line)
|
||||
logging.exception(
|
||||
"skipping line %d of file %s '%s'", ln, self.file, line
|
||||
)
|
||||
continue
|
||||
if ix is not None and iy is not None:
|
||||
self.addinitial(self.map(inodenum), ix, iy, iz)
|
||||
|
@ -1054,7 +1156,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
return sessfn
|
||||
|
||||
if self.session.user is not None:
|
||||
userfn = os.path.join('/home', self.session.user, '.core', 'configs', file_name)
|
||||
userfn = os.path.join(
|
||||
"/home", self.session.user, ".core", "configs", file_name
|
||||
)
|
||||
if os.path.exists(userfn):
|
||||
return userfn
|
||||
|
||||
|
@ -1100,16 +1204,22 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
|
||||
:return: nothing
|
||||
"""
|
||||
if self.autostart == '':
|
||||
if self.autostart == "":
|
||||
logging.info("not auto-starting ns-2 script for %s" % self.wlan.name)
|
||||
return
|
||||
try:
|
||||
t = float(self.autostart)
|
||||
except ValueError:
|
||||
logging.exception("Invalid auto-start seconds specified '%s' for %s", self.autostart, self.wlan.name)
|
||||
logging.exception(
|
||||
"Invalid auto-start seconds specified '%s' for %s",
|
||||
self.autostart,
|
||||
self.wlan.name,
|
||||
)
|
||||
return
|
||||
self.movenodesinitial()
|
||||
logging.info("scheduling ns-2 script for %s autostart at %s" % (self.wlan.name, t))
|
||||
logging.info(
|
||||
"scheduling ns-2 script for %s autostart at %s" % (self.wlan.name, t)
|
||||
)
|
||||
self.state = self.STATE_RUNNING
|
||||
self.session.event_loop.add_event(t, self.run)
|
||||
|
||||
|
@ -1167,8 +1277,10 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
filename = self.script_pause
|
||||
elif typestr == "stop":
|
||||
filename = self.script_stop
|
||||
if filename is None or filename == '':
|
||||
if filename is None or filename == "":
|
||||
return
|
||||
filename = self.findfile(filename)
|
||||
args = ["/bin/sh", filename, typestr]
|
||||
utils.check_cmd(args, cwd=self.session.session_dir, env=self.session.get_environment())
|
||||
utils.check_cmd(
|
||||
args, cwd=self.session.session_dir, env=self.session.get_environment()
|
||||
)
|
||||
|
|
|
@ -11,17 +11,14 @@ import signal
|
|||
import socket
|
||||
import string
|
||||
import threading
|
||||
from builtins import range
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from builtins import range
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core.emulator.data import NodeData, LinkData
|
||||
from core.emulator.enumerations import NodeTypes, LinkTypes
|
||||
from core.nodes import client, nodeutils, ipaddress
|
||||
from core.nodes.interface import TunTap, CoreInterface
|
||||
from core.nodes.interface import Veth
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.data import LinkData, NodeData
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import client, ipaddress, nodeutils
|
||||
from core.nodes.interface import CoreInterface, TunTap, Veth
|
||||
|
||||
_DEFAULT_MTU = 1500
|
||||
|
||||
|
@ -32,6 +29,7 @@ class NodeBase(object):
|
|||
"""
|
||||
Base class for CORE nodes (nodes and networks)
|
||||
"""
|
||||
|
||||
apitype = None
|
||||
|
||||
# TODO: appears start has no usage, verify and remove
|
||||
|
@ -199,7 +197,7 @@ class NodeBase(object):
|
|||
altitude=alt,
|
||||
model=model,
|
||||
emulation_server=emulation_server,
|
||||
services=services
|
||||
services=services,
|
||||
)
|
||||
|
||||
return node_data
|
||||
|
@ -236,16 +234,6 @@ class CoreNodeBase(NodeBase):
|
|||
self.nodedir = None
|
||||
self.tmpnodedir = False
|
||||
|
||||
def addservice(self, service):
|
||||
"""
|
||||
Add a services to the service list.
|
||||
|
||||
:param core.services.coreservices.CoreService service: service to add
|
||||
:return: nothing
|
||||
"""
|
||||
if service is not None:
|
||||
self.services.append(service)
|
||||
|
||||
def makenodedir(self):
|
||||
"""
|
||||
Create the node directory.
|
||||
|
@ -418,10 +406,13 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
Provides standard core node logic.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.DEFAULT.value
|
||||
valid_address_types = {"inet", "inet6", "inet6link"}
|
||||
|
||||
def __init__(self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True):
|
||||
def __init__(
|
||||
self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True
|
||||
):
|
||||
"""
|
||||
Create a CoreNode instance.
|
||||
|
||||
|
@ -434,7 +425,9 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
super(CoreNode, self).__init__(session, _id, name, start)
|
||||
self.nodedir = nodedir
|
||||
self.ctrlchnlname = os.path.abspath(os.path.join(self.session.session_dir, self.name))
|
||||
self.ctrlchnlname = os.path.abspath(
|
||||
os.path.join(self.session.session_dir, self.name)
|
||||
)
|
||||
self.client = None
|
||||
self.pid = None
|
||||
self.up = False
|
||||
|
@ -475,9 +468,12 @@ class CoreNode(CoreNodeBase):
|
|||
vnoded = [
|
||||
constants.VNODED_BIN,
|
||||
"-v",
|
||||
"-c", self.ctrlchnlname,
|
||||
"-l", self.ctrlchnlname + ".log",
|
||||
"-p", self.ctrlchnlname + ".pid"
|
||||
"-c",
|
||||
self.ctrlchnlname,
|
||||
"-l",
|
||||
self.ctrlchnlname + ".log",
|
||||
"-p",
|
||||
self.ctrlchnlname + ".pid",
|
||||
]
|
||||
if self.nodedir:
|
||||
vnoded += ["-C", self.nodedir]
|
||||
|
@ -612,7 +608,9 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
if path[0] != "/":
|
||||
raise ValueError("path not fully qualified: %s" % path)
|
||||
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip("/").replace("/", "."))
|
||||
hostpath = os.path.join(
|
||||
self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")
|
||||
)
|
||||
os.mkdir(hostpath)
|
||||
self.mount(hostpath, path)
|
||||
|
||||
|
@ -626,8 +624,13 @@ class CoreNode(CoreNodeBase):
|
|||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
source = os.path.abspath(source)
|
||||
logging.info("node(%s) mounting: %s at %s", self.name, source, target)
|
||||
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (target, constants.MOUNT_BIN, source, target)
|
||||
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
|
||||
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (
|
||||
target,
|
||||
constants.MOUNT_BIN,
|
||||
source,
|
||||
target,
|
||||
)
|
||||
status, output = self.client.shcmd_result(cmd)
|
||||
if status:
|
||||
raise CoreCommandError(status, cmd, output)
|
||||
|
@ -674,12 +677,20 @@ class CoreNode(CoreNodeBase):
|
|||
if len(name) >= 16:
|
||||
raise ValueError("interface name (%s) too long" % name)
|
||||
|
||||
veth = Veth(node=self, name=name, localname=localname, net=net, start=self.up)
|
||||
veth = Veth(
|
||||
node=self, name=name, localname=localname, net=net, start=self.up
|
||||
)
|
||||
|
||||
if self.up:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
|
||||
self.network_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
|
||||
self.network_cmd([constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]
|
||||
)
|
||||
self.network_cmd(
|
||||
[constants.IP_BIN, "link", "set", veth.name, "name", ifname]
|
||||
)
|
||||
self.network_cmd(
|
||||
[constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"]
|
||||
)
|
||||
|
||||
veth.name = ifname
|
||||
|
||||
|
@ -725,7 +736,9 @@ 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(
|
||||
node=self, name=name, localname=localname, net=net, start=self.up
|
||||
)
|
||||
|
||||
try:
|
||||
self.addnetif(tuntap, ifindex)
|
||||
|
@ -747,7 +760,15 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
self._netif[ifindex].sethwaddr(addr)
|
||||
if self.up:
|
||||
args = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)]
|
||||
args = [
|
||||
constants.IP_BIN,
|
||||
"link",
|
||||
"set",
|
||||
"dev",
|
||||
self.ifname(ifindex),
|
||||
"address",
|
||||
str(addr),
|
||||
]
|
||||
self.network_cmd(args)
|
||||
|
||||
def addaddr(self, ifindex, addr):
|
||||
|
@ -761,10 +782,26 @@ class CoreNode(CoreNodeBase):
|
|||
if self.up:
|
||||
# check if addr is ipv6
|
||||
if ":" in str(addr):
|
||||
args = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)]
|
||||
args = [
|
||||
constants.IP_BIN,
|
||||
"addr",
|
||||
"add",
|
||||
str(addr),
|
||||
"dev",
|
||||
self.ifname(ifindex),
|
||||
]
|
||||
self.network_cmd(args)
|
||||
else:
|
||||
args = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)]
|
||||
args = [
|
||||
constants.IP_BIN,
|
||||
"addr",
|
||||
"add",
|
||||
str(addr),
|
||||
"broadcast",
|
||||
"+",
|
||||
"dev",
|
||||
self.ifname(ifindex),
|
||||
]
|
||||
self.network_cmd(args)
|
||||
|
||||
self._netif[ifindex].addaddr(addr)
|
||||
|
@ -784,7 +821,16 @@ class CoreNode(CoreNodeBase):
|
|||
logging.exception("trying to delete unknown address: %s" % addr)
|
||||
|
||||
if self.up:
|
||||
self.network_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
|
||||
self.network_cmd(
|
||||
[
|
||||
constants.IP_BIN,
|
||||
"addr",
|
||||
"del",
|
||||
str(addr),
|
||||
"dev",
|
||||
self.ifname(ifindex),
|
||||
]
|
||||
)
|
||||
|
||||
def delalladdr(self, ifindex, address_types=None):
|
||||
"""
|
||||
|
@ -803,7 +849,9 @@ class CoreNode(CoreNodeBase):
|
|||
|
||||
for address_type in address_types:
|
||||
if address_type not in self.valid_address_types:
|
||||
raise ValueError("addr type must be in: %s" % " ".join(self.valid_address_types))
|
||||
raise ValueError(
|
||||
"addr type must be in: %s" % " ".join(self.valid_address_types)
|
||||
)
|
||||
for address in addresses[address_type]:
|
||||
self.deladdr(ifindex, address)
|
||||
|
||||
|
@ -818,7 +866,9 @@ class CoreNode(CoreNodeBase):
|
|||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
self.network_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
|
||||
self.network_cmd(
|
||||
[constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"]
|
||||
)
|
||||
|
||||
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
||||
"""
|
||||
|
@ -874,18 +924,41 @@ class CoreNode(CoreNodeBase):
|
|||
: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)])
|
||||
utils.check_cmd([constants.IP_BIN, "link", "add", "name", tmp1, "type", "veth", "peer", "name", tmp2])
|
||||
tmp1 = "tmp." + "".join(
|
||||
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||
)
|
||||
tmp2 = "tmp." + "".join(
|
||||
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||
)
|
||||
utils.check_cmd(
|
||||
[
|
||||
constants.IP_BIN,
|
||||
"link",
|
||||
"add",
|
||||
"name",
|
||||
tmp1,
|
||||
"type",
|
||||
"veth",
|
||||
"peer",
|
||||
"name",
|
||||
tmp2,
|
||||
]
|
||||
)
|
||||
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
|
||||
self.network_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
|
||||
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
|
||||
self.addnetif(interface, self.newifindex())
|
||||
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
|
||||
othernode.network_cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
|
||||
other_interface = CoreInterface(node=othernode, name=otherifname, mtu=_DEFAULT_MTU)
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)]
|
||||
)
|
||||
othernode.network_cmd(
|
||||
[constants.IP_BIN, "link", "set", tmp2, "name", otherifname]
|
||||
)
|
||||
other_interface = CoreInterface(
|
||||
node=othernode, name=otherifname, mtu=_DEFAULT_MTU
|
||||
)
|
||||
othernode.addnetif(other_interface, othernode.newifindex())
|
||||
|
||||
def addfile(self, srcname, filename):
|
||||
|
@ -948,7 +1021,9 @@ class CoreNode(CoreNodeBase):
|
|||
with self.opennodefile(filename, "w") as open_file:
|
||||
open_file.write(contents)
|
||||
os.chmod(open_file.name, mode)
|
||||
logging.info("node(%s) added file: %s; mode: 0%o", self.name, open_file.name, mode)
|
||||
logging.debug(
|
||||
"node(%s) added file: %s; mode: 0%o", self.name, open_file.name, mode
|
||||
)
|
||||
|
||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||
"""
|
||||
|
@ -964,13 +1039,16 @@ class CoreNode(CoreNodeBase):
|
|||
shutil.copy2(srcfilename, hostfilename)
|
||||
if mode is not None:
|
||||
os.chmod(hostfilename, mode)
|
||||
logging.info("node(%s) copied file: %s; mode: %s", self.name, hostfilename, mode)
|
||||
logging.info(
|
||||
"node(%s) copied file: %s; mode: %s", self.name, hostfilename, mode
|
||||
)
|
||||
|
||||
|
||||
class CoreNetworkBase(NodeBase):
|
||||
"""
|
||||
Base class for networks
|
||||
"""
|
||||
|
||||
linktype = LinkTypes.WIRED.value
|
||||
|
||||
def __init__(self, session, _id, name, start=True):
|
||||
|
@ -1048,9 +1126,9 @@ class CoreNetworkBase(NodeBase):
|
|||
linked_node = netif.othernet
|
||||
if linked_node.id == self.id:
|
||||
continue
|
||||
netif.swapparams('_params_up')
|
||||
netif.swapparams("_params_up")
|
||||
upstream_params = netif.getparams()
|
||||
netif.swapparams('_params_up')
|
||||
netif.swapparams("_params_up")
|
||||
if netif.getparams() != upstream_params:
|
||||
uni = True
|
||||
|
||||
|
@ -1092,7 +1170,7 @@ class CoreNetworkBase(NodeBase):
|
|||
bandwidth=netif.getparam("bw"),
|
||||
dup=netif.getparam("duplicate"),
|
||||
jitter=netif.getparam("jitter"),
|
||||
per=netif.getparam("loss")
|
||||
per=netif.getparam("loss"),
|
||||
)
|
||||
|
||||
all_links.append(link_data)
|
||||
|
@ -1100,7 +1178,7 @@ class CoreNetworkBase(NodeBase):
|
|||
if not uni:
|
||||
continue
|
||||
|
||||
netif.swapparams('_params_up')
|
||||
netif.swapparams("_params_up")
|
||||
link_data = LinkData(
|
||||
message_type=0,
|
||||
node1_id=linked_node.id,
|
||||
|
@ -1110,9 +1188,9 @@ class CoreNetworkBase(NodeBase):
|
|||
bandwidth=netif.getparam("bw"),
|
||||
dup=netif.getparam("duplicate"),
|
||||
jitter=netif.getparam("jitter"),
|
||||
per=netif.getparam("loss")
|
||||
per=netif.getparam("loss"),
|
||||
)
|
||||
netif.swapparams('_params_up')
|
||||
netif.swapparams("_params_up")
|
||||
|
||||
all_links.append(link_data)
|
||||
|
||||
|
|
|
@ -6,11 +6,9 @@ The control channel can be accessed via calls using the vcmd shell.
|
|||
|
||||
import logging
|
||||
import os
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core import CoreCommandError, constants, utils
|
||||
|
||||
|
||||
class VnodeClient(object):
|
||||
|
@ -137,7 +135,15 @@ class VnodeClient(object):
|
|||
:rtype: int
|
||||
"""
|
||||
args = utils.split_args(args)
|
||||
return os.spawnlp(os.P_WAIT, constants.VCMD_BIN, constants.VCMD_BIN, "-c", self.ctrlchnlname, "--", *args)
|
||||
return os.spawnlp(
|
||||
os.P_WAIT,
|
||||
constants.VCMD_BIN,
|
||||
constants.VCMD_BIN,
|
||||
"-c",
|
||||
self.ctrlchnlname,
|
||||
"--",
|
||||
*args
|
||||
)
|
||||
|
||||
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
||||
"""
|
||||
|
@ -177,11 +183,27 @@ class VnodeClient(object):
|
|||
:return: terminal command result
|
||||
:rtype: int
|
||||
"""
|
||||
args = ("xterm", "-ut", "-title", self.name, "-e", constants.VCMD_BIN, "-c", self.ctrlchnlname, "--", sh)
|
||||
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"])
|
||||
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"):
|
||||
|
|
|
@ -2,7 +2,7 @@ import json
|
|||
import logging
|
||||
import os
|
||||
|
||||
from core import utils, CoreCommandError
|
||||
from core import CoreCommandError, utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.base import CoreNode
|
||||
|
||||
|
@ -250,7 +250,7 @@ class DockerNode(CoreNode):
|
|||
:param str path: path to create
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("creating node dir: %s", path)
|
||||
logging.debug("creating node dir: %s", path)
|
||||
args = "mkdir -p {path}".format(path=path)
|
||||
self.check_cmd(args)
|
||||
|
||||
|
@ -263,7 +263,7 @@ class DockerNode(CoreNode):
|
|||
:return: nothing
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
logging.info("mounting source(%s) target(%s)", source, target)
|
||||
logging.debug("mounting source(%s) target(%s)", source, target)
|
||||
raise Exception("not supported")
|
||||
|
||||
def nodefile(self, filename, contents, mode=0o644):
|
||||
|
@ -275,8 +275,8 @@ class DockerNode(CoreNode):
|
|||
:param int mode: mode for file
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
|
||||
logging.info("nodefile filename(%s) mode(%s)", filename, mode)
|
||||
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)
|
||||
|
|
|
@ -4,11 +4,9 @@ virtual ethernet classes that implement the interfaces available under Linux.
|
|||
|
||||
import logging
|
||||
import time
|
||||
from builtins import int
|
||||
from builtins import range
|
||||
from builtins import int, range
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
|
||||
|
@ -221,8 +219,20 @@ class Veth(CoreInterface):
|
|||
:return: nothing
|
||||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
utils.check_cmd([constants.IP_BIN, "link", "add", "name", self.localname,
|
||||
"type", "veth", "peer", "name", self.name])
|
||||
utils.check_cmd(
|
||||
[
|
||||
constants.IP_BIN,
|
||||
"link",
|
||||
"add",
|
||||
"name",
|
||||
self.localname,
|
||||
"type",
|
||||
"veth",
|
||||
"peer",
|
||||
"name",
|
||||
self.name,
|
||||
]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||
self.up = True
|
||||
|
||||
|
@ -237,7 +247,9 @@ class Veth(CoreInterface):
|
|||
|
||||
if self.node:
|
||||
try:
|
||||
self.node.network_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
||||
self.node.network_cmd(
|
||||
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error shutting down interface")
|
||||
|
||||
|
@ -298,7 +310,9 @@ class TunTap(CoreInterface):
|
|||
return
|
||||
|
||||
try:
|
||||
self.node.network_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
||||
self.node.network_cmd(
|
||||
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error shutting down tunnel tap")
|
||||
|
||||
|
@ -396,8 +410,12 @@ class TunTap(CoreInterface):
|
|||
"""
|
||||
self.waitfordevicelocal()
|
||||
netns = str(self.node.pid)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
|
||||
self.node.network_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", self.localname, "netns", netns]
|
||||
)
|
||||
self.node.network_cmd(
|
||||
[constants.IP_BIN, "link", "set", self.localname, "name", self.name]
|
||||
)
|
||||
self.node.network_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
|
||||
|
||||
def setaddrs(self):
|
||||
|
@ -408,7 +426,9 @@ class TunTap(CoreInterface):
|
|||
"""
|
||||
self.waitfordevicenode()
|
||||
for addr in self.addrlist:
|
||||
self.node.network_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
||||
self.node.network_cmd(
|
||||
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
|
||||
)
|
||||
|
||||
|
||||
class GreTap(CoreInterface):
|
||||
|
@ -418,9 +438,19 @@ class GreTap(CoreInterface):
|
|||
having a MAC address. The MAC address is required for bridging.
|
||||
"""
|
||||
|
||||
def __init__(self, node=None, name=None, session=None, mtu=1458,
|
||||
remoteip=None, _id=None, localip=None, ttl=255,
|
||||
key=None, start=True):
|
||||
def __init__(
|
||||
self,
|
||||
node=None,
|
||||
name=None,
|
||||
session=None,
|
||||
mtu=1458,
|
||||
remoteip=None,
|
||||
_id=None,
|
||||
localip=None,
|
||||
ttl=255,
|
||||
key=None,
|
||||
start=True,
|
||||
):
|
||||
"""
|
||||
Creates a GreTap instance.
|
||||
|
||||
|
@ -440,7 +470,7 @@ class GreTap(CoreInterface):
|
|||
self.session = session
|
||||
if _id is None:
|
||||
# from PyCoreObj
|
||||
_id = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
|
||||
_id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF
|
||||
self.id = _id
|
||||
sessionid = self.session.short_session_id()
|
||||
# interface name on the local host machine
|
||||
|
@ -452,8 +482,16 @@ class GreTap(CoreInterface):
|
|||
|
||||
if remoteip is None:
|
||||
raise ValueError("missing remote IP required for GRE TAP device")
|
||||
args = [constants.IP_BIN, "link", "add", self.localname, "type", "gretap",
|
||||
"remote", str(remoteip)]
|
||||
args = [
|
||||
constants.IP_BIN,
|
||||
"link",
|
||||
"add",
|
||||
self.localname,
|
||||
"type",
|
||||
"gretap",
|
||||
"remote",
|
||||
str(remoteip),
|
||||
]
|
||||
if localip:
|
||||
args += ["local", str(localip)]
|
||||
if ttl:
|
||||
|
|
|
@ -6,10 +6,8 @@ import logging
|
|||
import random
|
||||
import socket
|
||||
import struct
|
||||
from builtins import bytes
|
||||
from builtins import range
|
||||
from socket import AF_INET
|
||||
from socket import AF_INET6
|
||||
from builtins import bytes, int, range
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
|
||||
class MacAddress(object):
|
||||
|
@ -45,13 +43,13 @@ class MacAddress(object):
|
|||
if not self.addr:
|
||||
return IpAddress.from_string("::")
|
||||
tmp = struct.unpack("!Q", "\x00\x00" + self.addr)[0]
|
||||
nic = long(tmp) & 0x000000FFFFFF
|
||||
oui = long(tmp) & 0xFFFFFF000000
|
||||
nic = int(tmp) & 0x000000FFFFFF
|
||||
oui = int(tmp) & 0xFFFFFF000000
|
||||
# toggle U/L bit
|
||||
oui ^= 0x020000000000
|
||||
# append EUI-48 octets
|
||||
oui = (oui << 16) | 0xFFFE000000
|
||||
return IpAddress(AF_INET6, struct.pack("!QQ", 0xfe80 << 48, oui | nic))
|
||||
return IpAddress(AF_INET6, struct.pack("!QQ", 0xFE80 << 48, oui | nic))
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, s):
|
||||
|
@ -158,7 +156,7 @@ class IpAddress(object):
|
|||
tmp = [x for x in bytearray(self.addr)]
|
||||
for i in range(len(tmp) - 1, -1, -1):
|
||||
x = tmp[i] + carry
|
||||
tmp[i] = x & 0xff
|
||||
tmp[i] = x & 0xFF
|
||||
carry = x >> 8
|
||||
if carry == 0:
|
||||
break
|
||||
|
@ -239,7 +237,7 @@ class IpPrefix(object):
|
|||
netmask = ((1 << self.prefixlen) - 1) << addrbits
|
||||
prefix = bytes(b"")
|
||||
for i in range(-1, -(addrbits >> 3) - 2, -1):
|
||||
prefix = bytes([self.prefix[i] & (netmask & 0xff)]) + prefix
|
||||
prefix = bytes([self.prefix[i] & (netmask & 0xFF)]) + prefix
|
||||
netmask >>= 8
|
||||
self.prefix = self.prefix[:i] + prefix
|
||||
|
||||
|
@ -265,7 +263,11 @@ class IpPrefix(object):
|
|||
elif self is other:
|
||||
return True
|
||||
else:
|
||||
return other.af == self.af and other.prefixlen == self.prefixlen and other.prefix == self.prefix
|
||||
return (
|
||||
other.af == self.af
|
||||
and other.prefixlen == self.prefixlen
|
||||
and other.prefix == self.prefix
|
||||
)
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
|
@ -316,15 +318,20 @@ class IpPrefix(object):
|
|||
if tmp in [-1, 0, 1] and self.addrlen == self.prefixlen:
|
||||
return IpAddress(self.af, self.prefix)
|
||||
|
||||
if tmp == 0 or tmp > (1 << (self.addrlen - self.prefixlen)) - 1 or (
|
||||
self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1):
|
||||
if (
|
||||
tmp == 0
|
||||
or tmp > (1 << (self.addrlen - self.prefixlen)) - 1
|
||||
or (
|
||||
self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1
|
||||
)
|
||||
):
|
||||
raise ValueError("invalid hostid for prefix %s: %s" % (self, hostid))
|
||||
|
||||
addr = bytes(b"")
|
||||
prefix_endpoint = -1
|
||||
for i in range(-1, -(self.addrlen >> 3) - 1, -1):
|
||||
prefix_endpoint = i
|
||||
addr = bytes([self.prefix[i] | (tmp & 0xff)]) + addr
|
||||
addr = bytes([self.prefix[i] | (tmp & 0xFF)]) + addr
|
||||
tmp >>= 8
|
||||
if not tmp:
|
||||
break
|
||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
|||
import os
|
||||
import time
|
||||
|
||||
from core import utils, CoreCommandError
|
||||
from core import CoreCommandError, utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.base import CoreNode
|
||||
|
||||
|
@ -16,10 +16,9 @@ class LxdClient(object):
|
|||
self._addr = {}
|
||||
|
||||
def create_container(self):
|
||||
utils.check_cmd("lxc launch {image} {name}".format(
|
||||
name=self.name,
|
||||
image=self.image
|
||||
))
|
||||
utils.check_cmd(
|
||||
"lxc launch {image} {name}".format(name=self.name, image=self.image)
|
||||
)
|
||||
data = self.get_info()
|
||||
self.pid = data["state"]["pid"]
|
||||
return self.pid
|
||||
|
@ -31,7 +30,9 @@ class LxdClient(object):
|
|||
raise CoreCommandError(status, args, output)
|
||||
data = json.loads(output)
|
||||
if not data:
|
||||
raise CoreCommandError(status, args, "LXC({name}) not present".format(name=self.name))
|
||||
raise CoreCommandError(
|
||||
status, args, "LXC({name}) not present".format(name=self.name)
|
||||
)
|
||||
return data[0]
|
||||
|
||||
def is_alive(self):
|
||||
|
@ -42,15 +43,10 @@ class LxdClient(object):
|
|||
return False
|
||||
|
||||
def stop_container(self):
|
||||
utils.check_cmd("lxc delete --force {name}".format(
|
||||
name=self.name
|
||||
))
|
||||
utils.check_cmd("lxc delete --force {name}".format(name=self.name))
|
||||
|
||||
def _cmd_args(self, cmd):
|
||||
return "lxc exec -nT {name} -- {cmd}".format(
|
||||
name=self.name,
|
||||
cmd=cmd
|
||||
)
|
||||
return "lxc exec -nT {name} -- {cmd}".format(name=self.name, cmd=cmd)
|
||||
|
||||
def cmd_output(self, cmd):
|
||||
if isinstance(cmd, list):
|
||||
|
@ -67,10 +63,7 @@ class LxdClient(object):
|
|||
return utils.cmd(args, wait)
|
||||
|
||||
def _ns_args(self, cmd):
|
||||
return "nsenter -t {pid} -m -u -i -p -n {cmd}".format(
|
||||
pid=self.pid,
|
||||
cmd=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):
|
||||
|
@ -91,9 +84,7 @@ class LxdClient(object):
|
|||
destination = os.path.join("/root/", destination)
|
||||
|
||||
args = "lxc file push {source} {name}/{destination}".format(
|
||||
source=source,
|
||||
name=self.name,
|
||||
destination=destination
|
||||
source=source, name=self.name, destination=destination
|
||||
)
|
||||
status, output = utils.cmd_output(args)
|
||||
if status:
|
||||
|
@ -137,7 +128,16 @@ class LxdClient(object):
|
|||
class LxcNode(CoreNode):
|
||||
apitype = NodeTypes.LXC.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,
|
||||
image=None,
|
||||
):
|
||||
"""
|
||||
Create a LxcNode instance.
|
||||
|
||||
|
@ -262,7 +262,7 @@ class LxcNode(CoreNode):
|
|||
:return: nothing
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
logging.info("mounting source(%s) target(%s)", source, target)
|
||||
logging.debug("mounting source(%s) target(%s)", source, target)
|
||||
raise Exception("not supported")
|
||||
|
||||
def nodefile(self, filename, contents, mode=0o644):
|
||||
|
@ -274,8 +274,8 @@ class LxcNode(CoreNode):
|
|||
:param int mode: mode for file
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
|
||||
logging.info("nodefile filename(%s) mode(%s)", filename, mode)
|
||||
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)
|
||||
|
@ -292,7 +292,9 @@ class LxcNode(CoreNode):
|
|||
:param int mode: mode to copy to
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode)
|
||||
logging.info(
|
||||
"node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode
|
||||
)
|
||||
raise Exception("not supported")
|
||||
|
||||
def addnetif(self, netif, ifindex):
|
||||
|
|
|
@ -9,21 +9,16 @@ import threading
|
|||
import time
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import NodeTypes, LinkTypes, RegisterTlvs
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.interface import GreTap
|
||||
from core.nodes.interface import Veth
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.interface import GreTap, Veth
|
||||
|
||||
utils.check_executables([
|
||||
constants.BRCTL_BIN,
|
||||
constants.IP_BIN,
|
||||
constants.EBTABLES_BIN,
|
||||
constants.TC_BIN
|
||||
])
|
||||
utils.check_executables(
|
||||
[constants.BRCTL_BIN, constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN]
|
||||
)
|
||||
|
||||
ebtables_lock = threading.Lock()
|
||||
|
||||
|
@ -34,6 +29,7 @@ class EbtablesQueue(object):
|
|||
atomic commits. This improves performance and reliability when there are
|
||||
many WLAN link updates.
|
||||
"""
|
||||
|
||||
# update rate is every 300ms
|
||||
rate = 0.3
|
||||
# ebtables
|
||||
|
@ -83,7 +79,9 @@ class EbtablesQueue(object):
|
|||
try:
|
||||
del self.last_update_time[wlan]
|
||||
except KeyError:
|
||||
logging.exception("error deleting last update time for wlan, ignored before: %s", wlan)
|
||||
logging.exception(
|
||||
"error deleting last update time for wlan, ignored before: %s", wlan
|
||||
)
|
||||
|
||||
if len(self.last_update_time) > 0:
|
||||
return
|
||||
|
@ -149,7 +147,7 @@ class EbtablesQueue(object):
|
|||
# TODO: if these are WlanNodes, this will never throw an exception
|
||||
try:
|
||||
wlan.session
|
||||
except:
|
||||
except Exception:
|
||||
# Just mark as updated to remove from self.updates.
|
||||
self.updated(wlan)
|
||||
continue
|
||||
|
@ -168,7 +166,7 @@ class EbtablesQueue(object):
|
|||
:return: nothing
|
||||
"""
|
||||
# save kernel ebtables snapshot to a file
|
||||
args = self.ebatomiccmd(["--atomic-save", ])
|
||||
args = self.ebatomiccmd(["--atomic-save"])
|
||||
utils.check_cmd(args)
|
||||
|
||||
# modify the table file using queued ebtables commands
|
||||
|
@ -178,7 +176,7 @@ class EbtablesQueue(object):
|
|||
self.cmds = []
|
||||
|
||||
# commit the table file to the kernel
|
||||
args = self.ebatomiccmd(["--atomic-commit", ])
|
||||
args = self.ebatomiccmd(["--atomic-commit"])
|
||||
utils.check_cmd(args)
|
||||
|
||||
try:
|
||||
|
@ -205,20 +203,60 @@ class EbtablesQueue(object):
|
|||
"""
|
||||
with wlan._linked_lock:
|
||||
# flush the chain
|
||||
self.cmds.extend([["-F", wlan.brname], ])
|
||||
self.cmds.extend([["-F", 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"]])
|
||||
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",
|
||||
],
|
||||
]
|
||||
)
|
||||
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"]])
|
||||
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 global object because all WLANs share the same queue
|
||||
|
@ -243,6 +281,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
"""
|
||||
Provides linux bridge network functionality for core nodes.
|
||||
"""
|
||||
|
||||
policy = "DROP"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||
|
@ -282,10 +321,21 @@ class CoreNetwork(CoreNetworkBase):
|
|||
utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"])
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "up"])
|
||||
# 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]
|
||||
])
|
||||
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,
|
||||
],
|
||||
],
|
||||
)
|
||||
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
|
||||
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
|
||||
if os.path.exists(snoop):
|
||||
|
@ -308,10 +358,21 @@ class CoreNetwork(CoreNetworkBase):
|
|||
try:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
|
||||
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
|
||||
ebtablescmds(utils.check_cmd, [
|
||||
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.brname, "-j", self.brname],
|
||||
[constants.EBTABLES_BIN, "-X", self.brname]
|
||||
])
|
||||
ebtablescmds(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-D",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.brname,
|
||||
"-j",
|
||||
self.brname,
|
||||
],
|
||||
[constants.EBTABLES_BIN, "-X", self.brname],
|
||||
],
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error during shutdown")
|
||||
|
||||
|
@ -333,7 +394,9 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, netif.localname])
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "addif", self.brname, netif.localname]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
|
||||
|
||||
CoreNetworkBase.attach(self, netif)
|
||||
|
@ -346,7 +409,9 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, netif.localname])
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "delif", self.brname, netif.localname]
|
||||
)
|
||||
|
||||
CoreNetworkBase.detach(self, netif)
|
||||
|
||||
|
@ -411,8 +476,17 @@ class CoreNetwork(CoreNetworkBase):
|
|||
|
||||
ebq.ebchange(self)
|
||||
|
||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
|
||||
jitter=None, netif2=None, devname=None):
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
devname=None,
|
||||
):
|
||||
"""
|
||||
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||
|
||||
|
@ -436,12 +510,13 @@ class CoreNetwork(CoreNetworkBase):
|
|||
if bw is not None:
|
||||
burst = max(2 * netif.mtu, bw / 1000)
|
||||
# max IP payload
|
||||
limit = 0xffff
|
||||
tbf = ["tbf", "rate", str(bw),
|
||||
"burst", str(burst), "limit", str(limit)]
|
||||
limit = 0xFFFF
|
||||
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
||||
if bw > 0:
|
||||
if self.up:
|
||||
logging.debug("linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],))
|
||||
logging.debug(
|
||||
"linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],)
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||
netif.setparam("has_tbf", True)
|
||||
changed = True
|
||||
|
@ -496,7 +571,9 @@ class CoreNetwork(CoreNetworkBase):
|
|||
netif.setparam("has_netem", False)
|
||||
elif len(netem) > 1:
|
||||
if self.up:
|
||||
logging.debug("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
||||
logging.debug(
|
||||
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||
netif.setparam("has_netem", True)
|
||||
|
||||
|
@ -528,7 +605,9 @@ 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(
|
||||
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
||||
)
|
||||
self.attach(netif)
|
||||
if net.up:
|
||||
# this is similar to net.attach() but uses netif.name instead
|
||||
|
@ -569,7 +648,9 @@ class CoreNetwork(CoreNetworkBase):
|
|||
return
|
||||
|
||||
for addr in addrlist:
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname]
|
||||
)
|
||||
|
||||
|
||||
class GreTapBridge(CoreNetwork):
|
||||
|
@ -578,8 +659,18 @@ class GreTapBridge(CoreNetwork):
|
|||
another system.
|
||||
"""
|
||||
|
||||
def __init__(self, session, remoteip=None, _id=None, name=None,
|
||||
policy="ACCEPT", localip=None, ttl=255, key=None, start=True):
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
remoteip=None,
|
||||
_id=None,
|
||||
name=None,
|
||||
policy="ACCEPT",
|
||||
localip=None,
|
||||
ttl=255,
|
||||
key=None,
|
||||
start=True,
|
||||
):
|
||||
"""
|
||||
Create a GreTapBridge instance.
|
||||
|
||||
|
@ -593,7 +684,9 @@ class GreTapBridge(CoreNetwork):
|
|||
:param key: gre tap key
|
||||
:param bool start: start flag
|
||||
"""
|
||||
CoreNetwork.__init__(self, session=session, _id=_id, name=name, policy=policy, start=False)
|
||||
CoreNetwork.__init__(
|
||||
self, session=session, _id=_id, name=name, policy=policy, start=False
|
||||
)
|
||||
self.grekey = key
|
||||
if self.grekey is None:
|
||||
self.grekey = self.session.id ^ self.id
|
||||
|
@ -605,8 +698,14 @@ class GreTapBridge(CoreNetwork):
|
|||
if remoteip is None:
|
||||
self.gretap = None
|
||||
else:
|
||||
self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
|
||||
localip=localip, ttl=ttl, key=self.grekey)
|
||||
self.gretap = GreTap(
|
||||
node=self,
|
||||
session=session,
|
||||
remoteip=remoteip,
|
||||
localip=localip,
|
||||
ttl=ttl,
|
||||
key=self.grekey,
|
||||
)
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
|
@ -648,8 +747,13 @@ class GreTapBridge(CoreNetwork):
|
|||
localip = None
|
||||
if len(addrlist) > 1:
|
||||
localip = addrlist[1].split("/")[0]
|
||||
self.gretap = GreTap(session=self.session, remoteip=remoteip,
|
||||
localip=localip, ttl=self.ttl, key=self.grekey)
|
||||
self.gretap = GreTap(
|
||||
session=self.session,
|
||||
remoteip=remoteip,
|
||||
localip=localip,
|
||||
ttl=self.ttl,
|
||||
key=self.grekey,
|
||||
)
|
||||
self.attach(self.gretap)
|
||||
|
||||
def setkey(self, key):
|
||||
|
@ -667,6 +771,7 @@ class CtrlNet(CoreNetwork):
|
|||
"""
|
||||
Control network functionality.
|
||||
"""
|
||||
|
||||
policy = "ACCEPT"
|
||||
# base control interface index
|
||||
CTRLIF_IDX_BASE = 99
|
||||
|
@ -674,12 +779,21 @@ class CtrlNet(CoreNetwork):
|
|||
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"
|
||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
|
||||
]
|
||||
|
||||
def __init__(self, session, _id="ctrlnet", name=None, prefix=None,
|
||||
hostid=None, start=True, assign_address=True,
|
||||
updown_script=None, serverintf=None):
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
_id="ctrlnet",
|
||||
name=None,
|
||||
prefix=None,
|
||||
hostid=None,
|
||||
start=True,
|
||||
assign_address=True,
|
||||
updown_script=None,
|
||||
serverintf=None,
|
||||
):
|
||||
"""
|
||||
Creates a CtrlNet instance.
|
||||
|
||||
|
@ -726,12 +840,18 @@ class CtrlNet(CoreNetwork):
|
|||
logging.info("address %s", addr)
|
||||
|
||||
if self.updown_script:
|
||||
logging.info("interface %s updown script (%s startup) called", self.brname, self.updown_script)
|
||||
logging.info(
|
||||
"interface %s updown script (%s startup) called",
|
||||
self.brname,
|
||||
self.updown_script,
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
||||
|
||||
if self.serverintf:
|
||||
# sets the interface as a port of the bridge
|
||||
utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, self.serverintf])
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "addif", self.brname, self.serverintf]
|
||||
)
|
||||
|
||||
# bring interface up
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
||||
|
@ -758,7 +878,9 @@ class CtrlNet(CoreNetwork):
|
|||
logging.error(
|
||||
"error: An active control net bridge (%s) found. "
|
||||
"An older session might still be running. "
|
||||
"Stop all sessions and, if needed, delete %s to continue.", oldbr, oldbr
|
||||
"Stop all sessions and, if needed, delete %s to continue.",
|
||||
oldbr,
|
||||
oldbr,
|
||||
)
|
||||
return True
|
||||
return False
|
||||
|
@ -771,13 +893,23 @@ class CtrlNet(CoreNetwork):
|
|||
"""
|
||||
if self.serverintf is not None:
|
||||
try:
|
||||
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, self.serverintf])
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "delif", self.brname, self.serverintf]
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error deleting server interface %s from bridge %s", self.serverintf, self.brname)
|
||||
logging.exception(
|
||||
"error deleting server interface %s from bridge %s",
|
||||
self.serverintf,
|
||||
self.brname,
|
||||
)
|
||||
|
||||
if self.updown_script is not None:
|
||||
try:
|
||||
logging.info("interface %s updown script (%s shutdown) called", self.brname, self.updown_script)
|
||||
logging.info(
|
||||
"interface %s updown script (%s shutdown) called",
|
||||
self.brname,
|
||||
self.updown_script,
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.brname, "shutdown"])
|
||||
except CoreCommandError:
|
||||
logging.exception("error issuing shutdown script shutdown")
|
||||
|
@ -799,6 +931,7 @@ class PtpNet(CoreNetwork):
|
|||
"""
|
||||
Peer to peer network node.
|
||||
"""
|
||||
|
||||
policy = "ACCEPT"
|
||||
|
||||
def attach(self, netif):
|
||||
|
@ -809,7 +942,9 @@ class PtpNet(CoreNetwork):
|
|||
:return: nothing
|
||||
"""
|
||||
if len(self._netif) >= 2:
|
||||
raise ValueError("Point-to-point links support at most 2 network interfaces")
|
||||
raise ValueError(
|
||||
"Point-to-point links support at most 2 network interfaces"
|
||||
)
|
||||
|
||||
CoreNetwork.attach(self, netif)
|
||||
|
||||
|
@ -924,7 +1059,7 @@ class PtpNet(CoreNetwork):
|
|||
jitter=if2.getparam("jitter"),
|
||||
unidirectional=1,
|
||||
interface1_id=if2.node.getifindex(if2),
|
||||
interface2_id=if1.node.getifindex(if1)
|
||||
interface2_id=if1.node.getifindex(if1),
|
||||
)
|
||||
all_links.append(link_data)
|
||||
|
||||
|
@ -935,6 +1070,7 @@ class SwitchNode(CoreNetwork):
|
|||
"""
|
||||
Provides switch functionality within a core node.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.SWITCH.value
|
||||
policy = "ACCEPT"
|
||||
type = "lanswitch"
|
||||
|
@ -945,6 +1081,7 @@ class HubNode(CoreNetwork):
|
|||
Provides hub functionality within a core node, forwards packets to all bridge
|
||||
ports by turning off MAC address learning.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.HUB.value
|
||||
policy = "ACCEPT"
|
||||
type = "hub"
|
||||
|
@ -970,6 +1107,7 @@ class WlanNode(CoreNetwork):
|
|||
"""
|
||||
Provides wireless lan functionality within a core node.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.WIRELESS_LAN.value
|
||||
linktype = LinkTypes.WIRELESS.value
|
||||
policy = "DROP"
|
||||
|
@ -1015,9 +1153,14 @@ class WlanNode(CoreNetwork):
|
|||
:param dict config: configuration for model being set
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("adding model: %s", model.name)
|
||||
logging.debug("node(%s) setting model: %s", self.name, model.name)
|
||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||
self.model = model(session=self.session, _id=self.id)
|
||||
for netif in self.netifs():
|
||||
netif.poshook = self.model.position_callback
|
||||
if netif.poshook and netif.node:
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
self.updatemodel(config)
|
||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||
self.mobility = model(session=self.session, _id=self.id)
|
||||
|
@ -1031,14 +1174,14 @@ class WlanNode(CoreNetwork):
|
|||
def updatemodel(self, config):
|
||||
if not self.model:
|
||||
raise ValueError("no model set to update for node(%s)", self.id)
|
||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
||||
logging.debug(
|
||||
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||
)
|
||||
self.model.update_config(config)
|
||||
if self.model.position_callback:
|
||||
for netif in self.netifs():
|
||||
netif.poshook = self.model.position_callback
|
||||
if netif.node is not None:
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
for netif in self.netifs():
|
||||
if netif.poshook and netif.node:
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
|
||||
def all_link_data(self, flags):
|
||||
"""
|
||||
|
@ -1060,6 +1203,7 @@ class TunnelNode(GreTapBridge):
|
|||
"""
|
||||
Provides tunnel functionality in a core node.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.TUNNEL.value
|
||||
policy = "ACCEPT"
|
||||
type = "tunnel"
|
||||
|
|
|
@ -6,11 +6,10 @@ import core.nodes.docker
|
|||
import core.nodes.lxd
|
||||
import core.nodes.network
|
||||
import core.nodes.physical
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emane.nodes import EmaneNet, EmaneNode
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.network import GreTapBridge
|
||||
from core.nodes import physical
|
||||
from core.nodes.network import GreTapBridge
|
||||
|
||||
# legacy core nodes, that leverage linux bridges
|
||||
NODES = {
|
||||
|
@ -29,5 +28,5 @@ NODES = {
|
|||
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
|
||||
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet,
|
||||
NodeTypes.DOCKER: core.nodes.docker.DockerNode,
|
||||
NodeTypes.LXC: core.nodes.lxd.LxcNode
|
||||
NodeTypes.LXC: core.nodes.lxd.LxcNode,
|
||||
}
|
||||
|
|
|
@ -5,21 +5,15 @@ TODO: probably goes away, or implement the usage of "unshare", or docker formal.
|
|||
import logging
|
||||
import socket
|
||||
import threading
|
||||
from socket import AF_INET
|
||||
from socket import AF_INET6
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.interface import GreTap
|
||||
from core.nodes.interface import Veth
|
||||
from core.nodes.network import EbtablesQueue
|
||||
from core.nodes.network import GreTapBridge
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.interface import GreTap, Veth
|
||||
from core.nodes.network import EbtablesQueue, GreTapBridge
|
||||
|
||||
# a global object because all WLANs share the same queue
|
||||
# cannot have multiple threads invoking the ebtables commnd
|
||||
|
@ -27,11 +21,7 @@ ebtables_queue = EbtablesQueue()
|
|||
|
||||
ebtables_lock = threading.Lock()
|
||||
|
||||
utils.check_executables([
|
||||
constants.IP_BIN,
|
||||
constants.EBTABLES_BIN,
|
||||
constants.TC_BIN
|
||||
])
|
||||
utils.check_executables([constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN])
|
||||
|
||||
|
||||
def ebtables_commands(call, commands):
|
||||
|
@ -89,10 +79,21 @@ class OvsNet(CoreNetworkBase):
|
|||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
|
||||
|
||||
# create a new ebtables chain for this bridge
|
||||
ebtables_commands(utils.check_cmd, [
|
||||
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
|
||||
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name]
|
||||
])
|
||||
ebtables_commands(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-A",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.bridge_name,
|
||||
"-j",
|
||||
self.bridge_name,
|
||||
],
|
||||
],
|
||||
)
|
||||
|
||||
self.up = True
|
||||
|
||||
|
@ -106,10 +107,21 @@ class OvsNet(CoreNetworkBase):
|
|||
try:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
|
||||
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
|
||||
ebtables_commands(utils.check_cmd, [
|
||||
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name],
|
||||
[constants.EBTABLES_BIN, "-X", self.bridge_name]
|
||||
])
|
||||
ebtables_commands(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-D",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.bridge_name,
|
||||
"-j",
|
||||
self.bridge_name,
|
||||
],
|
||||
[constants.EBTABLES_BIN, "-X", self.bridge_name],
|
||||
],
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error bringing bridge down and removing it")
|
||||
|
||||
|
@ -124,14 +136,20 @@ class OvsNet(CoreNetworkBase):
|
|||
|
||||
def attach(self, interface):
|
||||
if self.up:
|
||||
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, interface.localname])
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.localname, "up"])
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "add-port", self.bridge_name, interface.localname]
|
||||
)
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", interface.localname, "up"]
|
||||
)
|
||||
|
||||
CoreNetworkBase.attach(self, interface)
|
||||
|
||||
def detach(self, interface):
|
||||
if self.up:
|
||||
utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, interface.localname])
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "del-port", self.bridge_name, interface.localname]
|
||||
)
|
||||
|
||||
CoreNetworkBase.detach(self, interface)
|
||||
|
||||
|
@ -183,8 +201,17 @@ class OvsNet(CoreNetworkBase):
|
|||
|
||||
ebtables_queue.ebchange(self)
|
||||
|
||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
|
||||
jitter=None, netif2=None, devname=None):
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
devname=None,
|
||||
):
|
||||
"""
|
||||
Configure link parameters by applying tc queuing disciplines on the
|
||||
interface.
|
||||
|
@ -202,9 +229,19 @@ class OvsNet(CoreNetworkBase):
|
|||
if bw > 0:
|
||||
if self.up:
|
||||
burst = max(2 * netif.mtu, bw / 1000)
|
||||
limit = 0xffff # max IP payload
|
||||
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
||||
logging.info("linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf])
|
||||
limit = 0xFFFF # max IP payload
|
||||
tbf = [
|
||||
"tbf",
|
||||
"rate",
|
||||
str(bw),
|
||||
"burst",
|
||||
str(burst),
|
||||
"limit",
|
||||
str(limit),
|
||||
]
|
||||
logging.info(
|
||||
"linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf]
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||
netif.setparam("has_tbf", True)
|
||||
elif netif.getparam("has_tbf") and bw <= 0:
|
||||
|
@ -234,7 +271,15 @@ class OvsNet(CoreNetworkBase):
|
|||
jitter_changed = netif.setparam("jitter", jitter)
|
||||
|
||||
# if nothing changed return
|
||||
if not any([bandwidth_changed, delay_changed, loss_changed, duplicate_changed, jitter_changed]):
|
||||
if not any(
|
||||
[
|
||||
bandwidth_changed,
|
||||
delay_changed,
|
||||
loss_changed,
|
||||
duplicate_changed,
|
||||
jitter_changed,
|
||||
]
|
||||
):
|
||||
return
|
||||
|
||||
# jitter and delay use the same delay statement
|
||||
|
@ -265,7 +310,9 @@ class OvsNet(CoreNetworkBase):
|
|||
netif.setparam("has_netem", False)
|
||||
elif len(netem) > 1:
|
||||
if self.up:
|
||||
logging.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
||||
logging.info(
|
||||
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||
netif.setparam("has_netem", True)
|
||||
|
||||
|
@ -295,11 +342,15 @@ class OvsNet(CoreNetworkBase):
|
|||
if len(name) >= 16:
|
||||
raise ValueError("interface name %s too long" % name)
|
||||
|
||||
interface = Veth(node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up)
|
||||
interface = Veth(
|
||||
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
||||
)
|
||||
self.attach(interface)
|
||||
if network.up:
|
||||
# this is similar to net.attach() but uses netif.name instead of localname
|
||||
utils.check_cmd([constants.OVS_BIN, "add-port", network.bridge_name, interface.name])
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "add-port", network.bridge_name, interface.name]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
||||
|
||||
network.attach(interface)
|
||||
|
@ -326,7 +377,9 @@ class OvsNet(CoreNetworkBase):
|
|||
return
|
||||
|
||||
for address in addresses:
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name]
|
||||
)
|
||||
|
||||
|
||||
class OvsCtrlNet(OvsNet):
|
||||
|
@ -336,11 +389,21 @@ class OvsCtrlNet(OvsNet):
|
|||
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"
|
||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
|
||||
]
|
||||
|
||||
def __init__(self, session, _id="ctrlnet", name=None, prefix=None, hostid=None,
|
||||
start=True, assign_address=True, updown_script=None, serverintf=None):
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
_id="ctrlnet",
|
||||
name=None,
|
||||
prefix=None,
|
||||
hostid=None,
|
||||
start=True,
|
||||
assign_address=True,
|
||||
updown_script=None,
|
||||
serverintf=None,
|
||||
):
|
||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||
self.hostid = hostid
|
||||
self.assign_address = assign_address
|
||||
|
@ -358,7 +421,10 @@ class OvsCtrlNet(OvsNet):
|
|||
else:
|
||||
addr = self.prefix.max_addr()
|
||||
|
||||
message = "Added control network bridge: %s %s" % (self.bridge_name, self.prefix)
|
||||
message = "Added control network bridge: %s %s" % (
|
||||
self.bridge_name,
|
||||
self.prefix,
|
||||
)
|
||||
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
||||
if self.assign_address:
|
||||
self.addrconfig(addresses=addresses)
|
||||
|
@ -366,11 +432,16 @@ class OvsCtrlNet(OvsNet):
|
|||
logging.info(message)
|
||||
|
||||
if self.updown_script:
|
||||
logging.info("interface %s updown script %s startup called" % (self.bridge_name, self.updown_script))
|
||||
logging.info(
|
||||
"interface %s updown script %s startup called"
|
||||
% (self.bridge_name, self.updown_script)
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
|
||||
|
||||
if self.serverintf:
|
||||
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf])
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
||||
|
||||
def detectoldbridge(self):
|
||||
|
@ -385,7 +456,10 @@ class OvsCtrlNet(OvsNet):
|
|||
for line in output.split("\n"):
|
||||
bride_name = line.split(".")
|
||||
if bride_name[0] == "b" and bride_name[1] == self.id:
|
||||
logging.error("older session may still be running with conflicting id for bridge: %s", line)
|
||||
logging.error(
|
||||
"older session may still be running with conflicting id for bridge: %s",
|
||||
line,
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -393,14 +467,23 @@ class OvsCtrlNet(OvsNet):
|
|||
def shutdown(self):
|
||||
if self.serverintf:
|
||||
try:
|
||||
utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf])
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf]
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error deleting server interface %s to controlnet bridge %s",
|
||||
self.serverintf, self.bridge_name)
|
||||
logging.exception(
|
||||
"error deleting server interface %s to controlnet bridge %s",
|
||||
self.serverintf,
|
||||
self.bridge_name,
|
||||
)
|
||||
|
||||
if self.updown_script:
|
||||
try:
|
||||
logging.info("interface %s updown script (%s shutdown) called", self.bridge_name, self.updown_script)
|
||||
logging.info(
|
||||
"interface %s updown script (%s shutdown) called",
|
||||
self.bridge_name,
|
||||
self.updown_script,
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"])
|
||||
except CoreCommandError:
|
||||
logging.exception("error during updown script shutdown")
|
||||
|
@ -419,7 +502,9 @@ class OvsPtpNet(OvsNet):
|
|||
|
||||
def attach(self, interface):
|
||||
if len(self._netif) >= 2:
|
||||
raise ValueError("point-to-point links support at most 2 network interfaces")
|
||||
raise ValueError(
|
||||
"point-to-point links support at most 2 network interfaces"
|
||||
)
|
||||
OvsNet.attach(self, interface)
|
||||
|
||||
def data(self, message_type, lat=None, lon=None, alt=None):
|
||||
|
@ -522,7 +607,7 @@ class OvsPtpNet(OvsNet):
|
|||
jitter=if1.getparam("jitter"),
|
||||
unidirectional=1,
|
||||
interface1_id=if2.node.getifindex(if2),
|
||||
interface2_id=if1.node.getifindex(if1)
|
||||
interface2_id=if1.node.getifindex(if1),
|
||||
)
|
||||
all_links.append(link_data)
|
||||
|
||||
|
@ -550,7 +635,9 @@ class OvsHubNode(OvsNet):
|
|||
if start:
|
||||
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
|
||||
# TODO: replace "brctl setageing 0"
|
||||
utils.check_cmd([constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"])
|
||||
utils.check_cmd(
|
||||
[constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"]
|
||||
)
|
||||
|
||||
|
||||
class OvsWlanNode(OvsNet):
|
||||
|
@ -602,7 +689,9 @@ class OvsWlanNode(OvsNet):
|
|||
def updatemodel(self, config):
|
||||
if not self.model:
|
||||
raise ValueError("no model set to update for node(%s)", self.id)
|
||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
||||
logging.info(
|
||||
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||
)
|
||||
self.model.set_configs(config, node_id=self.id)
|
||||
if self.model.position_callback:
|
||||
for netif in self.netifs():
|
||||
|
@ -633,9 +722,21 @@ class OvsGreTapBridge(OvsNet):
|
|||
another system.
|
||||
"""
|
||||
|
||||
def __init__(self, session, remoteip=None, _id=None, name=None, policy="ACCEPT",
|
||||
localip=None, ttl=255, key=None, start=True):
|
||||
OvsNet.__init__(self, session=session, _id=_id, name=name, policy=policy, start=False)
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
remoteip=None,
|
||||
_id=None,
|
||||
name=None,
|
||||
policy="ACCEPT",
|
||||
localip=None,
|
||||
ttl=255,
|
||||
key=None,
|
||||
start=True,
|
||||
):
|
||||
OvsNet.__init__(
|
||||
self, session=session, _id=_id, name=name, policy=policy, start=False
|
||||
)
|
||||
self.grekey = key
|
||||
if self.grekey is None:
|
||||
self.grekey = self.session.id ^ self.id
|
||||
|
@ -649,8 +750,14 @@ class OvsGreTapBridge(OvsNet):
|
|||
if remoteip is None:
|
||||
self.gretap = None
|
||||
else:
|
||||
self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
|
||||
localip=localip, ttl=ttl, key=self.grekey)
|
||||
self.gretap = GreTap(
|
||||
node=self,
|
||||
session=session,
|
||||
remoteip=remoteip,
|
||||
localip=localip,
|
||||
ttl=ttl,
|
||||
key=self.grekey,
|
||||
)
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
|
@ -690,8 +797,13 @@ class OvsGreTapBridge(OvsNet):
|
|||
if len(addresses) > 1:
|
||||
localip = addresses[1].split("/")[0]
|
||||
|
||||
self.gretap = GreTap(session=self.session, remoteip=remoteip,
|
||||
localip=localip, ttl=self.ttl, key=self.grekey)
|
||||
self.gretap = GreTap(
|
||||
session=self.session,
|
||||
remoteip=remoteip,
|
||||
localip=localip,
|
||||
ttl=self.ttl,
|
||||
key=self.grekey,
|
||||
)
|
||||
self.attach(self.gretap)
|
||||
|
||||
def setkey(self, key):
|
||||
|
@ -709,5 +821,5 @@ OVS_NODES = {
|
|||
NodeTypes.TUNNEL: OvsTunnelNode,
|
||||
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
|
||||
NodeTypes.PEER_TO_PEER: OvsPtpNet,
|
||||
NodeTypes.CONTROL_NET: OvsCtrlNet
|
||||
NodeTypes.CONTROL_NET: OvsCtrlNet,
|
||||
}
|
||||
|
|
|
@ -7,13 +7,11 @@ import os
|
|||
import subprocess
|
||||
import threading
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.network import GreTap
|
||||
from core.nodes.network import CoreNetwork
|
||||
from core.nodes.network import CoreNetwork, GreTap
|
||||
|
||||
|
||||
class PhysicalNode(CoreNodeBase):
|
||||
|
@ -104,14 +102,25 @@ class PhysicalNode(CoreNodeBase):
|
|||
self._netif[ifindex].sethwaddr(addr)
|
||||
ifname = self.ifname(ifindex)
|
||||
if self.up:
|
||||
self.check_cmd([constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)])
|
||||
self.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)]
|
||||
)
|
||||
|
||||
def addaddr(self, ifindex, addr):
|
||||
"""
|
||||
Add an address to an interface.
|
||||
"""
|
||||
if self.up:
|
||||
self.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)])
|
||||
self.check_cmd(
|
||||
[
|
||||
constants.IP_BIN,
|
||||
"addr",
|
||||
"add",
|
||||
str(addr),
|
||||
"dev",
|
||||
self.ifname(ifindex),
|
||||
]
|
||||
)
|
||||
|
||||
self._netif[ifindex].addaddr(addr)
|
||||
|
||||
|
@ -125,7 +134,16 @@ class PhysicalNode(CoreNodeBase):
|
|||
logging.exception("trying to delete unknown address: %s", addr)
|
||||
|
||||
if self.up:
|
||||
self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
|
||||
self.check_cmd(
|
||||
[
|
||||
constants.IP_BIN,
|
||||
"addr",
|
||||
"del",
|
||||
str(addr),
|
||||
"dev",
|
||||
self.ifname(ifindex),
|
||||
]
|
||||
)
|
||||
|
||||
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
||||
"""
|
||||
|
@ -140,8 +158,12 @@ class PhysicalNode(CoreNodeBase):
|
|||
|
||||
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
||||
if self.up:
|
||||
self.check_cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "down"])
|
||||
self.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "name", netif.name])
|
||||
self.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", "dev", netif.localname, "down"]
|
||||
)
|
||||
self.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", netif.localname, "name", netif.name]
|
||||
)
|
||||
|
||||
netif.localname = netif.name
|
||||
|
||||
|
@ -152,16 +174,35 @@ class PhysicalNode(CoreNodeBase):
|
|||
self.addaddr(ifindex, addr)
|
||||
|
||||
if self.up:
|
||||
self.check_cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "up"])
|
||||
self.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", "dev", netif.localname, "up"]
|
||||
)
|
||||
|
||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
):
|
||||
"""
|
||||
Apply tc queing disciplines using LxBrNet.linkconfig()
|
||||
"""
|
||||
# borrow the tc qdisc commands from LxBrNet.linkconfig()
|
||||
linux_bridge = CoreNetwork(session=self.session, start=False)
|
||||
linux_bridge.up = True
|
||||
linux_bridge.linkconfig(netif, bw=bw, delay=delay, loss=loss, duplicate=duplicate, jitter=jitter, netif2=netif2)
|
||||
linux_bridge.linkconfig(
|
||||
netif,
|
||||
bw=bw,
|
||||
delay=delay,
|
||||
loss=loss,
|
||||
duplicate=duplicate,
|
||||
jitter=jitter,
|
||||
netif2=netif2,
|
||||
)
|
||||
del linux_bridge
|
||||
|
||||
def newifindex(self):
|
||||
|
@ -188,7 +229,9 @@ class PhysicalNode(CoreNodeBase):
|
|||
# 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)
|
||||
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)
|
||||
|
@ -205,7 +248,9 @@ class PhysicalNode(CoreNodeBase):
|
|||
def privatedir(self, path):
|
||||
if path[0] != "/":
|
||||
raise ValueError("path not fully qualified: %s" % path)
|
||||
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip('/').replace('/', '.'))
|
||||
hostpath = os.path.join(
|
||||
self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")
|
||||
)
|
||||
os.mkdir(hostpath)
|
||||
self.mount(hostpath, path)
|
||||
|
||||
|
@ -251,6 +296,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
RJ45Node is a physical interface on the host linked to the emulated
|
||||
network.
|
||||
"""
|
||||
|
||||
apitype = NodeTypes.RJ45.value
|
||||
type = "rj45"
|
||||
|
||||
|
@ -304,7 +350,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
try:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "down"])
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "flush", "dev", self.localname])
|
||||
utils.check_cmd([constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
||||
utils.check_cmd(
|
||||
[constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"]
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error shutting down")
|
||||
|
||||
|
@ -427,7 +475,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
if self.up:
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
|
||||
)
|
||||
|
||||
CoreInterface.addaddr(self, addr)
|
||||
|
||||
|
@ -440,7 +490,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
if self.up:
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "addr", "del", str(addr), "dev", self.name]
|
||||
)
|
||||
|
||||
CoreInterface.deladdr(self, addr)
|
||||
|
||||
|
@ -481,9 +533,22 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
"""
|
||||
for addr in self.old_addrs:
|
||||
if addr[1] is None:
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname])
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname]
|
||||
)
|
||||
else:
|
||||
utils.check_cmd([constants.IP_BIN, "addr", "add", addr[0], "brd", addr[1], "dev", self.localname])
|
||||
utils.check_cmd(
|
||||
[
|
||||
constants.IP_BIN,
|
||||
"addr",
|
||||
"add",
|
||||
addr[0],
|
||||
"brd",
|
||||
addr[1],
|
||||
"dev",
|
||||
self.localname,
|
||||
]
|
||||
)
|
||||
|
||||
if self.old_up:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||
|
|
|
@ -4,18 +4,21 @@ sdt.py: Scripted Display Tool (SDT3D) helper
|
|||
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from future.moves.urllib.parse import urlparse
|
||||
|
||||
from core import constants
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import LinkTlvs
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import NodeTlvs
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import (
|
||||
EventTypes,
|
||||
LinkTlvs,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTlvs,
|
||||
NodeTypes,
|
||||
)
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import NodeBase, CoreNetworkBase
|
||||
from core.nodes.base import CoreNetworkBase, NodeBase
|
||||
|
||||
|
||||
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
||||
|
@ -40,6 +43,7 @@ class Sdt(object):
|
|||
The connect() method initializes the display, and can be invoked
|
||||
when a node position or link has changed.
|
||||
"""
|
||||
|
||||
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
|
||||
# default altitude (in meters) for flyto view
|
||||
DEFAULT_ALT = 2500
|
||||
|
@ -93,7 +97,12 @@ class Sdt(object):
|
|||
alt = node_data.altitude
|
||||
|
||||
if all([lat, lon, alt]):
|
||||
self.updatenodegeo(node_data.id, node_data.latitude, node_data.longitude, node_data.altitude)
|
||||
self.updatenodegeo(
|
||||
node_data.id,
|
||||
node_data.latitude,
|
||||
node_data.longitude,
|
||||
node_data.altitude,
|
||||
)
|
||||
|
||||
if node_data.message_type == 0:
|
||||
# TODO: z is not currently supported by node messages
|
||||
|
@ -107,7 +116,12 @@ class Sdt(object):
|
|||
:return: nothing
|
||||
"""
|
||||
if link_data.link_type == LinkTypes.WIRELESS.value:
|
||||
self.updatelink(link_data.node1_id, link_data.node2_id, link_data.message_type, wireless=True)
|
||||
self.updatelink(
|
||||
link_data.node1_id,
|
||||
link_data.node2_id,
|
||||
link_data.message_type,
|
||||
wireless=True,
|
||||
)
|
||||
|
||||
def is_enabled(self):
|
||||
"""
|
||||
|
@ -179,7 +193,7 @@ class Sdt(object):
|
|||
:return: initialize command status
|
||||
:rtype: bool
|
||||
"""
|
||||
if not self.cmd("path \"%s/icons/normal\"" % constants.CORE_DATA_DIR):
|
||||
if not self.cmd('path "%s/icons/normal"' % constants.CORE_DATA_DIR):
|
||||
return False
|
||||
# send node type to icon mappings
|
||||
for node_type, icon in self.DEFAULT_SPRITES:
|
||||
|
@ -266,7 +280,9 @@ class Sdt(object):
|
|||
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
|
||||
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
|
||||
self.cmd("sprite %s image %s" % (type, icon))
|
||||
self.cmd("node %d type %s label on,\"%s\" %s" % (nodenum, node_type, name, pos))
|
||||
self.cmd(
|
||||
'node %d type %s label on,"%s" %s' % (nodenum, node_type, name, pos)
|
||||
)
|
||||
else:
|
||||
self.cmd("node %d %s" % (nodenum, pos))
|
||||
|
||||
|
@ -330,16 +346,29 @@ class Sdt(object):
|
|||
(x, y, z) = node.getposition()
|
||||
if x is None or y is None:
|
||||
continue
|
||||
self.updatenode(node.id, MessageFlags.ADD.value, x, y, z, node.name, node.type, node.icon)
|
||||
self.updatenode(
|
||||
node.id,
|
||||
MessageFlags.ADD.value,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
node.name,
|
||||
node.type,
|
||||
node.icon,
|
||||
)
|
||||
for nodenum in sorted(self.remotes.keys()):
|
||||
r = self.remotes[nodenum]
|
||||
x, y, z = r.pos
|
||||
self.updatenode(nodenum, MessageFlags.ADD.value, x, y, z, r.name, r.type, r.icon)
|
||||
self.updatenode(
|
||||
nodenum, MessageFlags.ADD.value, x, y, z, r.name, r.type, r.icon
|
||||
)
|
||||
|
||||
for net in nets:
|
||||
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
||||
for link_data in all_links:
|
||||
is_wireless = nodeutils.is_node(net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE))
|
||||
is_wireless = nodeutils.is_node(
|
||||
net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
||||
)
|
||||
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
||||
if is_wireless and link_data.node1_id == net.id:
|
||||
continue
|
||||
|
@ -348,7 +377,7 @@ class Sdt(object):
|
|||
link_data.node1_id,
|
||||
link_data.node2_id,
|
||||
MessageFlags.ADD.value,
|
||||
wireless_link
|
||||
wireless_link,
|
||||
)
|
||||
|
||||
for n1num in sorted(self.remotes.keys()):
|
||||
|
@ -397,8 +426,7 @@ class Sdt(object):
|
|||
icon = msg.get_tlv(NodeTlvs.ICON.value)
|
||||
|
||||
net = False
|
||||
if nodetype == NodeTypes.DEFAULT.value or \
|
||||
nodetype == NodeTypes.PHYSICAL.value:
|
||||
if nodetype == NodeTypes.DEFAULT.value or nodetype == NodeTypes.PHYSICAL.value:
|
||||
if model is None:
|
||||
model = "router"
|
||||
nodetype = model
|
||||
|
@ -413,7 +441,9 @@ class Sdt(object):
|
|||
except KeyError:
|
||||
node = None
|
||||
if node:
|
||||
self.updatenode(node.id, msg.flags, x, y, z, node.name, node.type, node.icon)
|
||||
self.updatenode(
|
||||
node.id, msg.flags, x, y, z, node.name, node.type, node.icon
|
||||
)
|
||||
else:
|
||||
if nodenum in self.remotes:
|
||||
remote = self.remotes[nodenum]
|
||||
|
@ -424,7 +454,14 @@ class Sdt(object):
|
|||
if icon is None:
|
||||
icon = remote.icon
|
||||
else:
|
||||
remote = Bunch(_id=nodenum, type=nodetype, icon=icon, name=name, net=net, links=set())
|
||||
remote = Bunch(
|
||||
_id=nodenum,
|
||||
type=nodetype,
|
||||
icon=icon,
|
||||
name=name,
|
||||
net=net,
|
||||
links=set(),
|
||||
)
|
||||
self.remotes[nodenum] = remote
|
||||
remote.pos = (x, y, z)
|
||||
self.updatenode(nodenum, msg.flags, x, y, z, name, nodetype, icon)
|
||||
|
|
|
@ -9,6 +9,7 @@ class Bird(CoreService):
|
|||
"""
|
||||
Bird router support
|
||||
"""
|
||||
|
||||
name = "bird"
|
||||
executables = ("bird",)
|
||||
group = "BIRD"
|
||||
|
@ -34,11 +35,11 @@ class Bird(CoreService):
|
|||
Helper to return the first IPv4 address of a node as its router ID.
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") >= 0:
|
||||
return a.split('/')[0]
|
||||
return a.split("/")[0]
|
||||
# raise ValueError, "no IPv4 address found for router ID"
|
||||
return "0.0.0.0"
|
||||
|
||||
|
@ -72,7 +73,10 @@ protocol device {
|
|||
scan time 10; # Scan interfaces every 10 seconds
|
||||
}
|
||||
|
||||
""" % (cls.name, cls.routerid(node))
|
||||
""" % (
|
||||
cls.name,
|
||||
cls.routerid(node),
|
||||
)
|
||||
|
||||
# Generate protocol specific configurations
|
||||
for s in node.services:
|
||||
|
@ -113,7 +117,7 @@ class BirdService(CoreService):
|
|||
cfg = ""
|
||||
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += ' interface "%s";\n' % ifc.name
|
||||
|
||||
|
@ -160,18 +164,18 @@ class BirdOspf(BirdService):
|
|||
|
||||
@classmethod
|
||||
def generatebirdconfig(cls, node):
|
||||
cfg = 'protocol ospf {\n'
|
||||
cfg += ' export filter {\n'
|
||||
cfg += ' if source = RTS_BGP then {\n'
|
||||
cfg += ' ospf_metric1 = 100;\n'
|
||||
cfg += ' accept;\n'
|
||||
cfg += ' }\n'
|
||||
cfg += ' accept;\n'
|
||||
cfg += ' };\n'
|
||||
cfg += ' area 0.0.0.0 {\n'
|
||||
cfg = "protocol ospf {\n"
|
||||
cfg += " export filter {\n"
|
||||
cfg += " if source = RTS_BGP then {\n"
|
||||
cfg += " ospf_metric1 = 100;\n"
|
||||
cfg += " accept;\n"
|
||||
cfg += " }\n"
|
||||
cfg += " accept;\n"
|
||||
cfg += " };\n"
|
||||
cfg += " area 0.0.0.0 {\n"
|
||||
cfg += cls.generatebirdifcconfig(node)
|
||||
cfg += ' };\n'
|
||||
cfg += '}\n\n'
|
||||
cfg += " };\n"
|
||||
cfg += "}\n\n"
|
||||
|
||||
return cfg
|
||||
|
||||
|
@ -185,21 +189,21 @@ class BirdRadv(BirdService):
|
|||
|
||||
@classmethod
|
||||
def generatebirdconfig(cls, node):
|
||||
cfg = '/* This is a sample config that must be customized */\n'
|
||||
cfg = "/* This is a sample config that must be customized */\n"
|
||||
|
||||
cfg += 'protocol radv {\n'
|
||||
cfg += ' # auto configuration on all interfaces\n'
|
||||
cfg += "protocol radv {\n"
|
||||
cfg += " # auto configuration on all interfaces\n"
|
||||
cfg += cls.generatebirdifcconfig(node)
|
||||
cfg += ' # Advertise DNS\n'
|
||||
cfg += ' rdnss {\n'
|
||||
cfg += '# lifetime mult 10;\n'
|
||||
cfg += '# lifetime mult 10;\n'
|
||||
cfg += '# ns 2001:0DB8:1234::11;\n'
|
||||
cfg += '# ns 2001:0DB8:1234::11;\n'
|
||||
cfg += '# ns 2001:0DB8:1234::12;\n'
|
||||
cfg += '# ns 2001:0DB8:1234::12;\n'
|
||||
cfg += ' };\n'
|
||||
cfg += '}\n\n'
|
||||
cfg += " # Advertise DNS\n"
|
||||
cfg += " rdnss {\n"
|
||||
cfg += "# lifetime mult 10;\n"
|
||||
cfg += "# lifetime mult 10;\n"
|
||||
cfg += "# ns 2001:0DB8:1234::11;\n"
|
||||
cfg += "# ns 2001:0DB8:1234::11;\n"
|
||||
cfg += "# ns 2001:0DB8:1234::12;\n"
|
||||
cfg += "# ns 2001:0DB8:1234::12;\n"
|
||||
cfg += " };\n"
|
||||
cfg += "}\n\n"
|
||||
|
||||
return cfg
|
||||
|
||||
|
@ -213,15 +217,15 @@ class BirdRip(BirdService):
|
|||
|
||||
@classmethod
|
||||
def generatebirdconfig(cls, node):
|
||||
cfg = 'protocol rip {\n'
|
||||
cfg += ' period 10;\n'
|
||||
cfg += ' garbage time 60;\n'
|
||||
cfg = "protocol rip {\n"
|
||||
cfg += " period 10;\n"
|
||||
cfg += " garbage time 60;\n"
|
||||
cfg += cls.generatebirdifcconfig(node)
|
||||
cfg += ' honor neighbor;\n'
|
||||
cfg += ' authentication none;\n'
|
||||
cfg += ' import all;\n'
|
||||
cfg += ' export all;\n'
|
||||
cfg += '}\n\n'
|
||||
cfg += " honor neighbor;\n"
|
||||
cfg += " authentication none;\n"
|
||||
cfg += " import all;\n"
|
||||
cfg += " export all;\n"
|
||||
cfg += "}\n\n"
|
||||
|
||||
return cfg
|
||||
|
||||
|
@ -236,10 +240,10 @@ class BirdStatic(BirdService):
|
|||
|
||||
@classmethod
|
||||
def generatebirdconfig(cls, node):
|
||||
cfg = '/* This is a sample config that must be customized */\n'
|
||||
cfg += 'protocol static {\n'
|
||||
cfg += '# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n'
|
||||
cfg += '# route 203.0.113.0/24 reject; # Sink route\n'
|
||||
cfg = "/* This is a sample config that must be customized */\n"
|
||||
cfg += "protocol static {\n"
|
||||
cfg += "# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n"
|
||||
cfg += "# route 203.0.113.0/24 reject; # Sink route\n"
|
||||
cfg += '# route 10.2.0.0/24 via "arc0"; # Secondary network\n'
|
||||
cfg += '}\n\n'
|
||||
cfg += "}\n\n"
|
||||
return cfg
|
||||
|
|
|
@ -15,8 +15,7 @@ from multiprocessing.pool import ThreadPool
|
|||
from core import CoreCommandError, utils
|
||||
from core.constants import which
|
||||
from core.emulator.data import FileData
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.emulator.enumerations import MessageFlags, RegisterTlvs
|
||||
|
||||
|
||||
class ServiceBootError(Exception):
|
||||
|
@ -62,7 +61,9 @@ class ServiceDependencies(object):
|
|||
for name in self.node_services:
|
||||
service = self.node_services[name]
|
||||
if service.name in self.booted:
|
||||
logging.debug("skipping service that will already be booted: %s", service.name)
|
||||
logging.debug(
|
||||
"skipping service that will already be booted: %s", service.name
|
||||
)
|
||||
continue
|
||||
|
||||
path = self._start(service)
|
||||
|
@ -70,7 +71,10 @@ class ServiceDependencies(object):
|
|||
paths.append(path)
|
||||
|
||||
if self.booted != set(self.node_services):
|
||||
raise ValueError("failure to boot all services: %s != %s" % (self.booted, self.node_services.keys()))
|
||||
raise ValueError(
|
||||
"failure to boot all services: %s != %s"
|
||||
% (self.booted, self.node_services.keys())
|
||||
)
|
||||
|
||||
return paths
|
||||
|
||||
|
@ -92,10 +96,16 @@ class ServiceDependencies(object):
|
|||
# dive down
|
||||
for service_name in current_service.dependencies:
|
||||
if service_name not in self.node_services:
|
||||
raise ValueError("required dependency was not included in node services: %s" % service_name)
|
||||
raise ValueError(
|
||||
"required dependency was not included in node services: %s"
|
||||
% service_name
|
||||
)
|
||||
|
||||
if service_name in self.visiting:
|
||||
raise ValueError("cyclic dependency at service(%s): %s" % (current_service.name, service_name))
|
||||
raise ValueError(
|
||||
"cyclic dependency at service(%s): %s"
|
||||
% (current_service.name, service_name)
|
||||
)
|
||||
|
||||
if service_name not in self.visited:
|
||||
service = self.node_services[service_name]
|
||||
|
@ -117,7 +127,16 @@ class ServiceDependencies(object):
|
|||
|
||||
|
||||
class ServiceShim(object):
|
||||
keys = ["dirs", "files", "startidx", "cmdup", "cmddown", "cmdval", "meta", "starttime"]
|
||||
keys = [
|
||||
"dirs",
|
||||
"files",
|
||||
"startidx",
|
||||
"cmdup",
|
||||
"cmddown",
|
||||
"cmdval",
|
||||
"meta",
|
||||
"starttime",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def tovaluelist(cls, node, service):
|
||||
|
@ -132,8 +151,16 @@ class ServiceShim(object):
|
|||
"""
|
||||
start_time = 0
|
||||
start_index = 0
|
||||
valmap = [service.dirs, service.configs, start_index, service.startup,
|
||||
service.shutdown, service.validate, service.meta, start_time]
|
||||
valmap = [
|
||||
service.dirs,
|
||||
service.configs,
|
||||
start_index,
|
||||
service.startup,
|
||||
service.shutdown,
|
||||
service.validate,
|
||||
service.meta,
|
||||
start_time,
|
||||
]
|
||||
if not service.custom:
|
||||
valmap[1] = service.get_configs(node)
|
||||
valmap[3] = service.get_startup(node)
|
||||
|
@ -201,16 +228,17 @@ class ServiceShim(object):
|
|||
:return: services
|
||||
:rtype: list
|
||||
"""
|
||||
servicesstring = opaque.split(':')
|
||||
servicesstring = opaque.split(":")
|
||||
if servicesstring[0] != "service":
|
||||
return []
|
||||
return servicesstring[1].split(',')
|
||||
return servicesstring[1].split(",")
|
||||
|
||||
|
||||
class ServiceManager(object):
|
||||
"""
|
||||
Manages services available for CORE nodes to use.
|
||||
"""
|
||||
|
||||
services = {}
|
||||
|
||||
@classmethod
|
||||
|
@ -231,8 +259,12 @@ class ServiceManager(object):
|
|||
# validate dependent executables are present
|
||||
for executable in service.executables:
|
||||
if not which(executable):
|
||||
logging.debug("service(%s) missing executable: %s", service.name, executable)
|
||||
raise ValueError("service(%s) missing executable: %s" % (service.name, executable))
|
||||
logging.debug(
|
||||
"service(%s) missing executable: %s", service.name, executable
|
||||
)
|
||||
raise ValueError(
|
||||
"service(%s) missing executable: %s" % (service.name, executable)
|
||||
)
|
||||
|
||||
# make service available
|
||||
cls.services[name] = service
|
||||
|
@ -280,6 +312,7 @@ class CoreServices(object):
|
|||
the default services configured for each node type, and any
|
||||
custom service configuration. A CoreService is not a Configurable.
|
||||
"""
|
||||
|
||||
name = "services"
|
||||
config_type = RegisterTlvs.UTILITY.value
|
||||
|
||||
|
@ -368,17 +401,20 @@ class CoreServices(object):
|
|||
:return: nothing
|
||||
"""
|
||||
if not services:
|
||||
logging.info("using default services for node(%s) type(%s)", node.name, node_type)
|
||||
logging.info(
|
||||
"using default services for node(%s) type(%s)", node.name, node_type
|
||||
)
|
||||
services = self.default_services.get(node_type, [])
|
||||
|
||||
logging.info("setting services for node(%s): %s", node.name, services)
|
||||
for service_name in services:
|
||||
service = self.get_service(node.id, service_name, default_service=True)
|
||||
if not service:
|
||||
logging.warning("unknown service(%s) for node(%s)", service_name, node.name)
|
||||
logging.warning(
|
||||
"unknown service(%s) for node(%s)", service_name, node.name
|
||||
)
|
||||
continue
|
||||
logging.info("adding service to node(%s): %s", node.name, service_name)
|
||||
node.addservice(service)
|
||||
node.services.append(service)
|
||||
|
||||
def all_configs(self):
|
||||
"""
|
||||
|
@ -445,11 +481,15 @@ class CoreServices(object):
|
|||
:param list[CoreService] boot_path: service to start in dependent order
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("booting node services: %s", " -> ".join([x.name for x in boot_path]))
|
||||
logging.info(
|
||||
"booting node(%s) services: %s",
|
||||
node.name,
|
||||
" -> ".join([x.name for x in boot_path]),
|
||||
)
|
||||
for service in boot_path:
|
||||
try:
|
||||
self.boot_service(node, service)
|
||||
except:
|
||||
except Exception:
|
||||
logging.exception("exception booting service: %s", service.name)
|
||||
raise
|
||||
|
||||
|
@ -462,16 +502,24 @@ class CoreServices(object):
|
|||
:param CoreService service: service to start
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("starting node(%s) service(%s) validation(%s)", node.name, service.name,
|
||||
service.validation_mode.name)
|
||||
logging.info(
|
||||
"starting node(%s) service(%s) validation(%s)",
|
||||
node.name,
|
||||
service.name,
|
||||
service.validation_mode.name,
|
||||
)
|
||||
|
||||
# create service directories
|
||||
for directory in service.dirs:
|
||||
try:
|
||||
node.privatedir(directory)
|
||||
except (CoreCommandError, ValueError) as e:
|
||||
logging.warning("error mounting private dir '%s' for service '%s': %s",
|
||||
directory, service.name, e)
|
||||
logging.warning(
|
||||
"error mounting private dir '%s' for service '%s': %s",
|
||||
directory,
|
||||
service.name,
|
||||
e,
|
||||
)
|
||||
|
||||
# create service files
|
||||
self.create_service_files(node, service)
|
||||
|
@ -480,7 +528,9 @@ class CoreServices(object):
|
|||
wait = service.validation_mode == ServiceMode.BLOCKING
|
||||
status = self.startup_service(node, service, wait)
|
||||
if status:
|
||||
raise ServiceBootError("node(%s) service(%s) error during startup" % (node.name, service.name))
|
||||
raise ServiceBootError(
|
||||
"node(%s) service(%s) error during startup" % (node.name, service.name)
|
||||
)
|
||||
|
||||
# blocking mode is finished
|
||||
if wait:
|
||||
|
@ -503,7 +553,9 @@ class CoreServices(object):
|
|||
time.sleep(service.validation_period)
|
||||
|
||||
if status:
|
||||
raise ServiceBootError("node(%s) service(%s) failed validation" % (node.name, service.name))
|
||||
raise ServiceBootError(
|
||||
"node(%s) service(%s) failed validation" % (node.name, service.name)
|
||||
)
|
||||
|
||||
def copy_service_file(self, node, filename, cfg):
|
||||
"""
|
||||
|
@ -517,9 +569,9 @@ class CoreServices(object):
|
|||
:return: True if successful, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
if cfg[:7] == 'file://':
|
||||
if cfg[:7] == "file://":
|
||||
src = cfg[7:]
|
||||
src = src.split('\n')[0]
|
||||
src = src.split("\n")[0]
|
||||
src = utils.expand_corepath(src, node.session, node)
|
||||
# TODO: glob here
|
||||
node.nodefilecopy(filename, src, mode=0o644)
|
||||
|
@ -546,7 +598,9 @@ class CoreServices(object):
|
|||
try:
|
||||
node.check_cmd(cmd)
|
||||
except CoreCommandError as e:
|
||||
logging.error("node(%s) service(%s) validate failed", node.name, service.name)
|
||||
logging.error(
|
||||
"node(%s) service(%s) validate failed", node.name, service.name
|
||||
)
|
||||
logging.error("cmd(%s): %s", e.cmd, e.output)
|
||||
status = -1
|
||||
break
|
||||
|
@ -603,7 +657,9 @@ class CoreServices(object):
|
|||
config_files = service.get_configs(node)
|
||||
|
||||
if filename not in config_files:
|
||||
raise ValueError("unknown service(%s) config file: %s", service_name, filename)
|
||||
raise ValueError(
|
||||
"unknown service(%s) config file: %s", service_name, filename
|
||||
)
|
||||
|
||||
# get the file data
|
||||
data = service.config_data.get(filename)
|
||||
|
@ -618,7 +674,7 @@ class CoreServices(object):
|
|||
node=node.id,
|
||||
name=filename,
|
||||
type=filetypestr,
|
||||
data=data
|
||||
data=data,
|
||||
)
|
||||
|
||||
def set_service_file(self, node_id, service_name, file_name, data):
|
||||
|
@ -645,7 +701,9 @@ class CoreServices(object):
|
|||
# validate file being set is valid
|
||||
config_files = service.configs
|
||||
if file_name not in config_files:
|
||||
logging.warning("received unknown file(%s) for service(%s)", file_name, service_name)
|
||||
logging.warning(
|
||||
"received unknown file(%s) for service(%s)", file_name, service_name
|
||||
)
|
||||
return
|
||||
|
||||
# set custom service file data
|
||||
|
@ -686,7 +744,6 @@ class CoreServices(object):
|
|||
:param CoreService service: service to reconfigure
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("node(%s) service(%s) creating config files", node.name, service.name)
|
||||
# get values depending on if custom or not
|
||||
config_files = service.configs
|
||||
if not service.custom:
|
||||
|
@ -739,6 +796,7 @@ class CoreService(object):
|
|||
"""
|
||||
Parent class used for defining services.
|
||||
"""
|
||||
|
||||
# service name should not include spaces
|
||||
name = None
|
||||
|
||||
|
|
|
@ -99,8 +99,7 @@ Limitations:
|
|||
|
||||
import logging
|
||||
|
||||
from core.services.coreservices import CoreService
|
||||
from core.services.coreservices import ServiceManager
|
||||
from core.services.coreservices import CoreService, ServiceManager
|
||||
|
||||
try:
|
||||
from docker import Client
|
||||
|
|
|
@ -23,15 +23,23 @@ class EmaneTransportService(CoreService):
|
|||
for interface in node.netifs(sort=True):
|
||||
network_node = node.session.get_node(interface.net.id)
|
||||
if nodeutils.is_node(network_node, NodeTypes.EMANE):
|
||||
config = node.session.emane.get_configs(network_node.id, network_node.model.name)
|
||||
config = node.session.emane.get_configs(
|
||||
network_node.id, network_node.model.name
|
||||
)
|
||||
if config and emanexml.is_external(config):
|
||||
nem_id = network_node.getnemid(interface)
|
||||
command = "emanetransportd -r -l 0 -d ../transportdaemon%s.xml" % nem_id
|
||||
command = (
|
||||
"emanetransportd -r -l 0 -d ../transportdaemon%s.xml"
|
||||
% nem_id
|
||||
)
|
||||
transport_commands.append(command)
|
||||
transport_commands = "\n".join(transport_commands)
|
||||
return """
|
||||
emanegentransportxml -o ../ ../platform%s.xml
|
||||
%s
|
||||
""" % (node.id, transport_commands)
|
||||
""" % (
|
||||
node.id,
|
||||
transport_commands,
|
||||
)
|
||||
else:
|
||||
raise ValueError
|
||||
|
|
|
@ -5,18 +5,14 @@ Assumes installation of FRR via https://deb.frrouting.org/
|
|||
|
||||
from core import constants
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import nodeutils, ipaddress
|
||||
from core.nodes import ipaddress, nodeutils
|
||||
from core.services.coreservices import CoreService
|
||||
|
||||
|
||||
class FRRZebra(CoreService):
|
||||
name = "FRRzebra"
|
||||
group = "FRR"
|
||||
dirs = (
|
||||
"/usr/local/etc/frr",
|
||||
"/var/run/frr",
|
||||
"/var/log/frr",
|
||||
)
|
||||
dirs = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr")
|
||||
configs = (
|
||||
"/usr/local/etc/frr/frr.conf",
|
||||
"frrboot.sh",
|
||||
|
@ -41,7 +37,9 @@ class FRRZebra(CoreService):
|
|||
elif filename == cls.configs[3]:
|
||||
return cls.generateFrrDaemons(node)
|
||||
else:
|
||||
raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs)
|
||||
raise ValueError(
|
||||
"file name (%s) is not a known configuration: %s", filename, cls.configs
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def generateVtyshConf(cls, node):
|
||||
|
@ -62,7 +60,7 @@ class FRRZebra(CoreService):
|
|||
for ifc in node.netifs():
|
||||
cfg += "interface %s\n" % ifc.name
|
||||
# include control interfaces in addressing but not routing daemons
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
cfg += " "
|
||||
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
||||
cfg += "\n"
|
||||
|
@ -84,13 +82,17 @@ class FRRZebra(CoreService):
|
|||
cfgv4 += ifccfg
|
||||
|
||||
if want_ipv4:
|
||||
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
|
||||
ipv4list = filter(
|
||||
lambda x: ipaddress.is_ipv4_address(x.split("/")[0]), ifc.addrlist
|
||||
)
|
||||
cfg += " "
|
||||
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
||||
cfg += "\n"
|
||||
cfg += cfgv4
|
||||
if want_ipv6:
|
||||
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
|
||||
ipv6list = filter(
|
||||
lambda x: ipaddress.is_ipv6_address(x.split("/")[0]), ifc.addrlist
|
||||
)
|
||||
cfg += " "
|
||||
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
||||
cfg += "\n"
|
||||
|
@ -120,10 +122,12 @@ class FRRZebra(CoreService):
|
|||
"""
|
||||
Generate a shell script used to boot the FRR daemons.
|
||||
"""
|
||||
frr_bin_search = node.session.options.get_config("frr_bin_search",
|
||||
default='"/usr/local/bin /usr/bin /usr/lib/frr"')
|
||||
frr_sbin_search = node.session.options.get_config('frr_sbin_search',
|
||||
default='"/usr/local/sbin /usr/sbin /usr/lib/frr"')
|
||||
frr_bin_search = node.session.options.get_config(
|
||||
"frr_bin_search", default='"/usr/local/bin /usr/bin /usr/lib/frr"'
|
||||
)
|
||||
frr_sbin_search = node.session.options.get_config(
|
||||
"frr_sbin_search", default='"/usr/local/sbin /usr/sbin /usr/lib/frr"'
|
||||
)
|
||||
return """\
|
||||
#!/bin/sh
|
||||
# auto-generated by zebra service (frr.py)
|
||||
|
@ -175,9 +179,8 @@ bootdaemon()
|
|||
flags="$flags -6"
|
||||
fi
|
||||
|
||||
|
||||
#force FRR to use CORE generated conf file
|
||||
flags="$flags -d -f $FRR_CONF"
|
||||
flags="$flags -d -f $FRR_CONF"
|
||||
$FRR_SBIN_DIR/$1 $flags
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
|
@ -203,7 +206,7 @@ bootfrr()
|
|||
|
||||
bootdaemon "zebra"
|
||||
for r in rip ripng ospf6 ospf bgp babel; do
|
||||
if grep -q "^router \<${r}\>" $FRR_CONF; then
|
||||
if grep -q "^router \\<${r}\\>" $FRR_CONF; then
|
||||
bootdaemon "${r}d"
|
||||
fi
|
||||
done
|
||||
|
@ -221,7 +224,12 @@ if [ "$1" != "zebra" ]; then
|
|||
fi
|
||||
confcheck
|
||||
bootfrr
|
||||
""" % (cls.configs[0], frr_sbin_search, frr_bin_search, constants.FRR_STATE_DIR)
|
||||
""" % (
|
||||
cls.configs[0],
|
||||
frr_sbin_search,
|
||||
frr_bin_search,
|
||||
constants.FRR_STATE_DIR,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def generateFrrDaemons(cls, node):
|
||||
|
@ -291,12 +299,12 @@ fabricd_options="-A 127.0.0.1"
|
|||
"""
|
||||
|
||||
|
||||
|
||||
class FrrService(CoreService):
|
||||
"""
|
||||
Parent class for FRR services. Defines properties and methods
|
||||
common to FRR's routing daemons.
|
||||
"""
|
||||
|
||||
name = None
|
||||
group = "FRR"
|
||||
dependencies = ("FRRzebra",)
|
||||
|
@ -315,11 +323,11 @@ class FrrService(CoreService):
|
|||
Helper to return the first IPv4 address of a node as its router ID.
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") >= 0:
|
||||
return a.split('/')[0]
|
||||
return a.split("/")[0]
|
||||
# raise ValueError, "no IPv4 address found for router ID"
|
||||
return "0.0.0.0"
|
||||
|
||||
|
@ -356,6 +364,7 @@ class FRROspfv2(FrrService):
|
|||
not build its own configuration file but has hooks for adding to the
|
||||
unified frr.conf file.
|
||||
"""
|
||||
|
||||
name = "FRROSPFv2"
|
||||
startup = ()
|
||||
shutdown = ("killall ospfd",)
|
||||
|
@ -397,7 +406,7 @@ class FRROspfv2(FrrService):
|
|||
cfg += " router-id %s\n" % rtrid
|
||||
# network 10.0.0.0/24 area 0
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") < 0:
|
||||
|
@ -431,6 +440,7 @@ class FRROspfv3(FrrService):
|
|||
not build its own configuration file but has hooks for adding to the
|
||||
unified frr.conf file.
|
||||
"""
|
||||
|
||||
name = "FRROSPFv3"
|
||||
startup = ()
|
||||
shutdown = ("killall ospf6d",)
|
||||
|
@ -481,7 +491,7 @@ class FRROspfv3(FrrService):
|
|||
rtrid = cls.routerid(node)
|
||||
cfg += " router-id %s\n" % rtrid
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
||||
cfg += "!\n"
|
||||
|
@ -511,6 +521,7 @@ class FRRBgp(FrrService):
|
|||
Peers must be manually configured, with a full mesh for those
|
||||
having the same AS number.
|
||||
"""
|
||||
|
||||
name = "FRRBGP"
|
||||
startup = ()
|
||||
shutdown = ("killall bgpd",)
|
||||
|
@ -536,6 +547,7 @@ class FRRRip(FrrService):
|
|||
"""
|
||||
The RIP service provides IPv4 routing for wired networks.
|
||||
"""
|
||||
|
||||
name = "FRRRIP"
|
||||
startup = ()
|
||||
shutdown = ("killall ripd",)
|
||||
|
@ -559,6 +571,7 @@ class FRRRipng(FrrService):
|
|||
"""
|
||||
The RIP NG service provides IPv6 routing for wired networks.
|
||||
"""
|
||||
|
||||
name = "FRRRIPNG"
|
||||
startup = ()
|
||||
shutdown = ("killall ripngd",)
|
||||
|
@ -583,6 +596,7 @@ class FRRBabel(FrrService):
|
|||
The Babel service provides a loop-avoiding distance-vector routing
|
||||
protocol for IPv6 and IPv4 with fast convergence properties.
|
||||
"""
|
||||
|
||||
name = "FRRBabel"
|
||||
startup = ()
|
||||
shutdown = ("killall babeld",)
|
||||
|
@ -611,28 +625,29 @@ class FRRpimd(FrrService):
|
|||
"""
|
||||
PIM multicast routing based on XORP.
|
||||
"""
|
||||
name = 'FRRpimd'
|
||||
|
||||
name = "FRRpimd"
|
||||
startup = ()
|
||||
shutdown = ('killall pimd',)
|
||||
validate = ('pidof pimd',)
|
||||
shutdown = ("killall pimd",)
|
||||
validate = ("pidof pimd",)
|
||||
ipv4_routing = True
|
||||
|
||||
@classmethod
|
||||
def generatefrrconfig(cls, node):
|
||||
ifname = 'eth0'
|
||||
ifname = "eth0"
|
||||
for ifc in node.netifs():
|
||||
if ifc.name != 'lo':
|
||||
if ifc.name != "lo":
|
||||
ifname = ifc.name
|
||||
break
|
||||
cfg = 'router mfea\n!\n'
|
||||
cfg += 'router igmp\n!\n'
|
||||
cfg += 'router pim\n'
|
||||
cfg += ' !ip pim rp-address 10.0.0.1\n'
|
||||
cfg += ' ip pim bsr-candidate %s\n' % ifname
|
||||
cfg += ' ip pim rp-candidate %s\n' % ifname
|
||||
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
|
||||
cfg = "router mfea\n!\n"
|
||||
cfg += "router igmp\n!\n"
|
||||
cfg += "router pim\n"
|
||||
cfg += " !ip pim rp-address 10.0.0.1\n"
|
||||
cfg += " ip pim bsr-candidate %s\n" % ifname
|
||||
cfg += " ip pim rp-candidate %s\n" % ifname
|
||||
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
def generatefrrifcconfig(cls, node, ifc):
|
||||
return ' ip mfea\n ip igmp\n ip pim\n'
|
||||
return " ip mfea\n ip igmp\n ip pim\n"
|
||||
|
|
|
@ -12,7 +12,8 @@ class NrlService(CoreService):
|
|||
"""
|
||||
Parent class for NRL services. Defines properties and methods
|
||||
common to NRL's routing daemons.
|
||||
"""""
|
||||
""" ""
|
||||
|
||||
name = None
|
||||
group = "ProtoSvc"
|
||||
dirs = ()
|
||||
|
@ -32,11 +33,11 @@ class NrlService(CoreService):
|
|||
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") >= 0:
|
||||
addr = a.split('/')[0]
|
||||
addr = a.split("/")[0]
|
||||
pre = Ipv4Prefix("%s/%s" % (addr, prefixlen))
|
||||
return str(pre)
|
||||
# raise ValueError, "no IPv4 address found"
|
||||
|
@ -63,13 +64,14 @@ class MgenSinkService(NrlService):
|
|||
def get_startup(cls, node):
|
||||
cmd = cls.startup[0]
|
||||
cmd += " output /tmp/mgen_%s.log" % node.name
|
||||
return cmd,
|
||||
return (cmd,)
|
||||
|
||||
|
||||
class NrlNhdp(NrlService):
|
||||
"""
|
||||
NeighborHood Discovery Protocol for MANET networks.
|
||||
"""
|
||||
|
||||
name = "NHDP"
|
||||
executables = ("nrlnhdp",)
|
||||
startup = ("nrlnhdp",)
|
||||
|
@ -90,19 +92,20 @@ class NrlNhdp(NrlService):
|
|||
cmd += " -flooding ecds"
|
||||
cmd += " -smfClient %s_smf" % node.name
|
||||
|
||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||
if len(netifs) > 0:
|
||||
interfacenames = map(lambda x: x.name, netifs)
|
||||
cmd += " -i "
|
||||
cmd += " -i ".join(interfacenames)
|
||||
|
||||
return cmd,
|
||||
return (cmd,)
|
||||
|
||||
|
||||
class NrlSmf(NrlService):
|
||||
"""
|
||||
Simplified Multicast Forwarding for MANET networks.
|
||||
"""
|
||||
|
||||
name = "SMF"
|
||||
executables = ("nrlsmf",)
|
||||
startup = ("sh startsmf.sh",)
|
||||
|
@ -111,7 +114,7 @@ class NrlSmf(NrlService):
|
|||
configs = ("startsmf.sh",)
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node, filename, ):
|
||||
def generate_config(cls, node, filename):
|
||||
"""
|
||||
Generate a startup script for SMF. Because nrlsmf does not
|
||||
daemonize, it can cause problems in some situations when launched
|
||||
|
@ -123,7 +126,7 @@ class NrlSmf(NrlService):
|
|||
cmd = "nrlsmf instance %s_smf" % node.name
|
||||
|
||||
servicenames = map(lambda x: x.name, node.services)
|
||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||
if len(netifs) == 0:
|
||||
return ""
|
||||
|
||||
|
@ -155,6 +158,7 @@ class NrlOlsr(NrlService):
|
|||
"""
|
||||
Optimized Link State Routing protocol for MANET networks.
|
||||
"""
|
||||
|
||||
name = "OLSR"
|
||||
executables = ("nrlolsrd",)
|
||||
startup = ("nrlolsrd",)
|
||||
|
@ -176,19 +180,20 @@ class NrlOlsr(NrlService):
|
|||
cmd += " -rpipe %s_olsr" % node.name
|
||||
|
||||
servicenames = map(lambda x: x.name, node.services)
|
||||
if "SMF" in servicenames and not "NHDP" in servicenames:
|
||||
if "SMF" in servicenames and "NHDP" not in servicenames:
|
||||
cmd += " -flooding s-mpr"
|
||||
cmd += " -smfClient %s_smf" % node.name
|
||||
if "zebra" in servicenames:
|
||||
cmd += " -z"
|
||||
|
||||
return cmd,
|
||||
return (cmd,)
|
||||
|
||||
|
||||
class NrlOlsrv2(NrlService):
|
||||
"""
|
||||
Optimized Link State Routing protocol version 2 for MANET networks.
|
||||
"""
|
||||
|
||||
name = "OLSRv2"
|
||||
executables = ("nrlolsrv2",)
|
||||
startup = ("nrlolsrv2",)
|
||||
|
@ -211,19 +216,20 @@ class NrlOlsrv2(NrlService):
|
|||
|
||||
cmd += " -p olsr"
|
||||
|
||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||
if len(netifs) > 0:
|
||||
interfacenames = map(lambda x: x.name, netifs)
|
||||
cmd += " -i "
|
||||
cmd += " -i ".join(interfacenames)
|
||||
|
||||
return cmd,
|
||||
return (cmd,)
|
||||
|
||||
|
||||
class OlsrOrg(NrlService):
|
||||
"""
|
||||
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
||||
"""
|
||||
|
||||
name = "OLSRORG"
|
||||
executables = ("olsrd",)
|
||||
configs = ("/etc/olsrd/olsrd.conf",)
|
||||
|
@ -238,13 +244,13 @@ class OlsrOrg(NrlService):
|
|||
Generate the appropriate command-line based on node interfaces.
|
||||
"""
|
||||
cmd = cls.startup[0]
|
||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||
if len(netifs) > 0:
|
||||
interfacenames = map(lambda x: x.name, netifs)
|
||||
cmd += " -i "
|
||||
cmd += " -i ".join(interfacenames)
|
||||
|
||||
return cmd,
|
||||
return (cmd,)
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node, filename):
|
||||
|
@ -582,7 +588,7 @@ class MgenActor(NrlService):
|
|||
dirs = ()
|
||||
# generated files (without a full path this file goes in the node's dir,
|
||||
# e.g. /tmp/pycore.12345/n1.conf/)
|
||||
configs = ('start_mgen_actor.sh',)
|
||||
configs = ("start_mgen_actor.sh",)
|
||||
# list of startup commands, also may be generated during startup
|
||||
startup = ("sh start_mgen_actor.sh",)
|
||||
# list of validation commands
|
||||
|
@ -614,6 +620,7 @@ class Arouted(NrlService):
|
|||
"""
|
||||
Adaptive Routing
|
||||
"""
|
||||
|
||||
name = "arouted"
|
||||
executables = ("arouted",)
|
||||
configs = ("startarouted.sh",)
|
||||
|
@ -626,7 +633,8 @@ class Arouted(NrlService):
|
|||
"""
|
||||
Return the Quagga.conf or quaggaboot.sh file contents.
|
||||
"""
|
||||
cfg = """
|
||||
cfg = (
|
||||
"""
|
||||
#!/bin/sh
|
||||
for f in "/tmp/%s_smf"; do
|
||||
count=1
|
||||
|
@ -640,7 +648,9 @@ for f in "/tmp/%s_smf"; do
|
|||
done
|
||||
done
|
||||
|
||||
""" % node.name
|
||||
"""
|
||||
% node.name
|
||||
)
|
||||
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
|
||||
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
|
||||
# seconds to consider a new route valid
|
||||
|
|
|
@ -4,7 +4,7 @@ quagga.py: defines routing services provided by Quagga.
|
|||
|
||||
from core import constants
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import nodeutils, ipaddress
|
||||
from core.nodes import ipaddress, nodeutils
|
||||
from core.services.coreservices import CoreService
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ class Zebra(CoreService):
|
|||
configs = (
|
||||
"/usr/local/etc/quagga/Quagga.conf",
|
||||
"quaggaboot.sh",
|
||||
"/usr/local/etc/quagga/vtysh.conf"
|
||||
"/usr/local/etc/quagga/vtysh.conf",
|
||||
)
|
||||
startup = ("sh quaggaboot.sh zebra",)
|
||||
shutdown = ("killall zebra",)
|
||||
|
@ -33,7 +33,9 @@ class Zebra(CoreService):
|
|||
elif filename == cls.configs[2]:
|
||||
return cls.generateVtyshConf(node)
|
||||
else:
|
||||
raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs)
|
||||
raise ValueError(
|
||||
"file name (%s) is not a known configuration: %s", filename, cls.configs
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def generateVtyshConf(cls, node):
|
||||
|
@ -54,7 +56,7 @@ class Zebra(CoreService):
|
|||
for ifc in node.netifs():
|
||||
cfg += "interface %s\n" % ifc.name
|
||||
# include control interfaces in addressing but not routing daemons
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
cfg += " "
|
||||
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
||||
cfg += "\n"
|
||||
|
@ -76,13 +78,17 @@ class Zebra(CoreService):
|
|||
cfgv4 += ifccfg
|
||||
|
||||
if want_ipv4:
|
||||
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
|
||||
ipv4list = filter(
|
||||
lambda x: ipaddress.is_ipv4_address(x.split("/")[0]), ifc.addrlist
|
||||
)
|
||||
cfg += " "
|
||||
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
||||
cfg += "\n"
|
||||
cfg += cfgv4
|
||||
if want_ipv6:
|
||||
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
|
||||
ipv6list = filter(
|
||||
lambda x: ipaddress.is_ipv6_address(x.split("/")[0]), ifc.addrlist
|
||||
)
|
||||
cfg += " "
|
||||
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
||||
cfg += "\n"
|
||||
|
@ -112,10 +118,12 @@ class Zebra(CoreService):
|
|||
"""
|
||||
Generate a shell script used to boot the Quagga daemons.
|
||||
"""
|
||||
quagga_bin_search = node.session.options.get_config("quagga_bin_search",
|
||||
default='"/usr/local/bin /usr/bin /usr/lib/quagga"')
|
||||
quagga_sbin_search = node.session.options.get_config('quagga_sbin_search',
|
||||
default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"')
|
||||
quagga_bin_search = node.session.options.get_config(
|
||||
"quagga_bin_search", default='"/usr/local/bin /usr/bin /usr/lib/quagga"'
|
||||
)
|
||||
quagga_sbin_search = node.session.options.get_config(
|
||||
"quagga_sbin_search", default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
|
||||
)
|
||||
return """\
|
||||
#!/bin/sh
|
||||
# auto-generated by zebra service (quagga.py)
|
||||
|
@ -191,7 +199,7 @@ bootquagga()
|
|||
|
||||
bootdaemon "zebra"
|
||||
for r in rip ripng ospf6 ospf bgp babel; do
|
||||
if grep -q "^router \<${r}\>" $QUAGGA_CONF; then
|
||||
if grep -q "^router \\<${r}\\>" $QUAGGA_CONF; then
|
||||
bootdaemon "${r}d"
|
||||
fi
|
||||
done
|
||||
|
@ -209,7 +217,12 @@ if [ "$1" != "zebra" ]; then
|
|||
fi
|
||||
confcheck
|
||||
bootquagga
|
||||
""" % (cls.configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
|
||||
""" % (
|
||||
cls.configs[0],
|
||||
quagga_sbin_search,
|
||||
quagga_bin_search,
|
||||
constants.QUAGGA_STATE_DIR,
|
||||
)
|
||||
|
||||
|
||||
class QuaggaService(CoreService):
|
||||
|
@ -217,6 +230,7 @@ class QuaggaService(CoreService):
|
|||
Parent class for Quagga services. Defines properties and methods
|
||||
common to Quagga's routing daemons.
|
||||
"""
|
||||
|
||||
name = None
|
||||
group = "Quagga"
|
||||
dependencies = ("zebra",)
|
||||
|
@ -235,11 +249,11 @@ class QuaggaService(CoreService):
|
|||
Helper to return the first IPv4 address of a node as its router ID.
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") >= 0:
|
||||
return a.split('/')[0]
|
||||
return a.split("/")[0]
|
||||
# raise ValueError, "no IPv4 address found for router ID"
|
||||
return "0.0.0.0"
|
||||
|
||||
|
@ -276,6 +290,7 @@ class Ospfv2(QuaggaService):
|
|||
not build its own configuration file but has hooks for adding to the
|
||||
unified Quagga.conf file.
|
||||
"""
|
||||
|
||||
name = "OSPFv2"
|
||||
startup = ()
|
||||
shutdown = ("killall ospfd",)
|
||||
|
@ -317,7 +332,7 @@ class Ospfv2(QuaggaService):
|
|||
cfg += " router-id %s\n" % rtrid
|
||||
# network 10.0.0.0/24 area 0
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") < 0:
|
||||
|
@ -351,6 +366,7 @@ class Ospfv3(QuaggaService):
|
|||
not build its own configuration file but has hooks for adding to the
|
||||
unified Quagga.conf file.
|
||||
"""
|
||||
|
||||
name = "OSPFv3"
|
||||
startup = ()
|
||||
shutdown = ("killall ospf6d",)
|
||||
|
@ -401,7 +417,7 @@ class Ospfv3(QuaggaService):
|
|||
rtrid = cls.routerid(node)
|
||||
cfg += " router-id %s\n" % rtrid
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
||||
cfg += "!\n"
|
||||
|
@ -432,6 +448,7 @@ class Ospfv3mdr(Ospfv3):
|
|||
configuration file but has hooks for adding to the
|
||||
unified Quagga.conf file.
|
||||
"""
|
||||
|
||||
name = "OSPFv3MDR"
|
||||
ipv4_routing = True
|
||||
|
||||
|
@ -440,8 +457,12 @@ class Ospfv3mdr(Ospfv3):
|
|||
cfg = cls.mtucheck(ifc)
|
||||
# Uncomment the following line to use Address Family Translation for IPv4
|
||||
cfg += " ipv6 ospf6 instance-id 65\n"
|
||||
if ifc.net is not None and nodeutils.is_node(ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||
return cfg + """\
|
||||
if ifc.net is not None and nodeutils.is_node(
|
||||
ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
||||
):
|
||||
return (
|
||||
cfg
|
||||
+ """\
|
||||
ipv6 ospf6 hello-interval 2
|
||||
ipv6 ospf6 dead-interval 6
|
||||
ipv6 ospf6 retransmit-interval 5
|
||||
|
@ -450,6 +471,7 @@ class Ospfv3mdr(Ospfv3):
|
|||
ipv6 ospf6 adjacencyconnectivity uniconnected
|
||||
ipv6 ospf6 lsafullness mincostlsa
|
||||
"""
|
||||
)
|
||||
else:
|
||||
return cfg
|
||||
|
||||
|
@ -460,6 +482,7 @@ class Bgp(QuaggaService):
|
|||
Peers must be manually configured, with a full mesh for those
|
||||
having the same AS number.
|
||||
"""
|
||||
|
||||
name = "BGP"
|
||||
startup = ()
|
||||
shutdown = ("killall bgpd",)
|
||||
|
@ -485,6 +508,7 @@ class Rip(QuaggaService):
|
|||
"""
|
||||
The RIP service provides IPv4 routing for wired networks.
|
||||
"""
|
||||
|
||||
name = "RIP"
|
||||
startup = ()
|
||||
shutdown = ("killall ripd",)
|
||||
|
@ -508,6 +532,7 @@ class Ripng(QuaggaService):
|
|||
"""
|
||||
The RIP NG service provides IPv6 routing for wired networks.
|
||||
"""
|
||||
|
||||
name = "RIPNG"
|
||||
startup = ()
|
||||
shutdown = ("killall ripngd",)
|
||||
|
@ -532,6 +557,7 @@ class Babel(QuaggaService):
|
|||
The Babel service provides a loop-avoiding distance-vector routing
|
||||
protocol for IPv6 and IPv4 with fast convergence properties.
|
||||
"""
|
||||
|
||||
name = "Babel"
|
||||
startup = ()
|
||||
shutdown = ("killall babeld",)
|
||||
|
@ -560,28 +586,29 @@ class Xpimd(QuaggaService):
|
|||
"""
|
||||
PIM multicast routing based on XORP.
|
||||
"""
|
||||
name = 'Xpimd'
|
||||
|
||||
name = "Xpimd"
|
||||
startup = ()
|
||||
shutdown = ('killall xpimd',)
|
||||
validate = ('pidof xpimd',)
|
||||
shutdown = ("killall xpimd",)
|
||||
validate = ("pidof xpimd",)
|
||||
ipv4_routing = True
|
||||
|
||||
@classmethod
|
||||
def generatequaggaconfig(cls, node):
|
||||
ifname = 'eth0'
|
||||
ifname = "eth0"
|
||||
for ifc in node.netifs():
|
||||
if ifc.name != 'lo':
|
||||
if ifc.name != "lo":
|
||||
ifname = ifc.name
|
||||
break
|
||||
cfg = 'router mfea\n!\n'
|
||||
cfg += 'router igmp\n!\n'
|
||||
cfg += 'router pim\n'
|
||||
cfg += ' !ip pim rp-address 10.0.0.1\n'
|
||||
cfg += ' ip pim bsr-candidate %s\n' % ifname
|
||||
cfg += ' ip pim rp-candidate %s\n' % ifname
|
||||
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
|
||||
cfg = "router mfea\n!\n"
|
||||
cfg += "router igmp\n!\n"
|
||||
cfg += "router pim\n"
|
||||
cfg += " !ip pim rp-address 10.0.0.1\n"
|
||||
cfg += " ip pim bsr-candidate %s\n" % ifname
|
||||
cfg += " ip pim rp-candidate %s\n" % ifname
|
||||
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
def generatequaggaifcconfig(cls, node, ifc):
|
||||
return ' ip mfea\n ip igmp\n ip pim\n'
|
||||
return " ip mfea\n ip igmp\n ip pim\n"
|
||||
|
|
|
@ -11,6 +11,7 @@ class SdnService(CoreService):
|
|||
"""
|
||||
Parent class for SDN services.
|
||||
"""
|
||||
|
||||
group = "SDN"
|
||||
|
||||
@classmethod
|
||||
|
@ -78,8 +79,14 @@ class OvsService(SdnService):
|
|||
for ifc in node.netifs():
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n" % (portnum, portnum + 1)
|
||||
cfg += "ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n" % (portnum + 1, portnum)
|
||||
cfg += (
|
||||
"ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n"
|
||||
% (portnum, portnum + 1)
|
||||
)
|
||||
cfg += (
|
||||
"ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n"
|
||||
% (portnum + 1, portnum)
|
||||
)
|
||||
portnum += 2
|
||||
|
||||
return cfg
|
||||
|
@ -102,5 +109,7 @@ class RyuService(SdnService):
|
|||
"""
|
||||
cfg = "#!/bin/sh\n"
|
||||
cfg += "# auto-generated by ryuService (ryuService.py)\n"
|
||||
cfg += "ryu-manager --observe-links ryu.app.ofctl_rest ryu.app.rest_topology &\n"
|
||||
cfg += (
|
||||
"ryu-manager --observe-links ryu.app.ofctl_rest ryu.app.rest_topology &\n"
|
||||
)
|
||||
return cfg
|
||||
|
|
|
@ -30,7 +30,9 @@ class VPNClient(CoreService):
|
|||
try:
|
||||
cfg += open(fname, "rb").read()
|
||||
except IOError:
|
||||
logging.exception("Error opening VPN client configuration template (%s)", fname)
|
||||
logging.exception(
|
||||
"Error opening VPN client configuration template (%s)", fname
|
||||
)
|
||||
|
||||
return cfg
|
||||
|
||||
|
@ -57,7 +59,9 @@ class VPNServer(CoreService):
|
|||
try:
|
||||
cfg += open(fname, "rb").read()
|
||||
except IOError:
|
||||
logging.exception("Error opening VPN server configuration template (%s)", fname)
|
||||
logging.exception(
|
||||
"Error opening VPN server configuration template (%s)", fname
|
||||
)
|
||||
|
||||
return cfg
|
||||
|
||||
|
@ -108,7 +112,9 @@ class Firewall(CoreService):
|
|||
try:
|
||||
cfg += open(fname, "rb").read()
|
||||
except IOError:
|
||||
logging.exception("Error opening Firewall configuration template (%s)", fname)
|
||||
logging.exception(
|
||||
"Error opening Firewall configuration template (%s)", fname
|
||||
)
|
||||
|
||||
return cfg
|
||||
|
||||
|
@ -117,10 +123,11 @@ class Nat(CoreService):
|
|||
"""
|
||||
IPv4 source NAT service.
|
||||
"""
|
||||
|
||||
name = "NAT"
|
||||
executables = ("iptables",)
|
||||
group = "Security"
|
||||
configs = ("nat.sh", )
|
||||
configs = ("nat.sh",)
|
||||
startup = ("sh nat.sh",)
|
||||
custom_needed = False
|
||||
|
||||
|
@ -130,7 +137,7 @@ class Nat(CoreService):
|
|||
Generate a NAT line for one interface.
|
||||
"""
|
||||
cfg = line_prefix + "iptables -t nat -A POSTROUTING -o "
|
||||
cfg +=ifc.name + " -j MASQUERADE\n"
|
||||
cfg += ifc.name + " -j MASQUERADE\n"
|
||||
|
||||
cfg += line_prefix + "iptables -A FORWARD -i " + ifc.name
|
||||
cfg += " -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
||||
|
@ -149,7 +156,7 @@ class Nat(CoreService):
|
|||
cfg += "# NAT out the first interface by default\n"
|
||||
have_nat = False
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control == True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
if have_nat:
|
||||
cfg += cls.generateifcnatrule(ifc, line_prefix="#")
|
||||
|
@ -159,4 +166,3 @@ class Nat(CoreService):
|
|||
cfg += cls.generateifcnatrule(ifc)
|
||||
cfg += "\n"
|
||||
return cfg
|
||||
|
||||
|
|
|
@ -12,7 +12,11 @@ class Ucarp(CoreService):
|
|||
group = "Utility"
|
||||
dirs = (UCARP_ETC,)
|
||||
configs = (
|
||||
UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
|
||||
UCARP_ETC + "/default.sh",
|
||||
UCARP_ETC + "/default-up.sh",
|
||||
UCARP_ETC + "/default-down.sh",
|
||||
"ucarpboot.sh",
|
||||
)
|
||||
startup = ("sh ucarpboot.sh",)
|
||||
shutdown = ("killall ucarp",)
|
||||
validate = ("pidof ucarp",)
|
||||
|
@ -39,7 +43,7 @@ class Ucarp(CoreService):
|
|||
Returns configuration file text.
|
||||
"""
|
||||
try:
|
||||
ucarp_bin = node.session.cfg['ucarp_bin']
|
||||
ucarp_bin = node.session.cfg["ucarp_bin"]
|
||||
except KeyError:
|
||||
ucarp_bin = "/usr/sbin/ucarp"
|
||||
|
||||
|
@ -100,14 +104,18 @@ STOP_SCRIPT=${UCARP_CFGDIR}/default-down.sh
|
|||
UCARP_OPTS="$OPTIONS -b $UCARP_BASE -k $SKEW -i $INTERFACE -v $INSTANCE_ID -p $PASSWORD -u $START_SCRIPT -d $STOP_SCRIPT -a $VIRTUAL_ADDRESS -s $SOURCE_ADDRESS -f $FACILITY $XPARAM"
|
||||
|
||||
${UCARP_EXEC} -B ${UCARP_OPTS}
|
||||
""" % (ucarp_bin, UCARP_ETC)
|
||||
""" % (
|
||||
ucarp_bin,
|
||||
UCARP_ETC,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def generateUcarpBoot(cls, node):
|
||||
"""
|
||||
Generate a shell script used to boot the Ucarp daemons.
|
||||
"""
|
||||
return """\
|
||||
return (
|
||||
"""\
|
||||
#!/bin/sh
|
||||
# Location of the UCARP config directory
|
||||
UCARP_CFGDIR=%s
|
||||
|
@ -117,7 +125,9 @@ chmod a+x ${UCARP_CFGDIR}/*.sh
|
|||
# Start the default ucarp daemon configuration
|
||||
${UCARP_CFGDIR}/default.sh
|
||||
|
||||
""" % UCARP_ETC
|
||||
"""
|
||||
% UCARP_ETC
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def generateVipUp(cls, node):
|
||||
|
@ -133,7 +143,7 @@ exec 2> /dev/null
|
|||
IP="${2}"
|
||||
NET="${3}"
|
||||
if [ -z "$NET" ]; then
|
||||
NET="24"
|
||||
NET="24"
|
||||
fi
|
||||
|
||||
/sbin/ip addr add ${IP}/${NET} dev "$1"
|
||||
|
@ -155,7 +165,7 @@ exec 2> /dev/null
|
|||
IP="${2}"
|
||||
NET="${3}"
|
||||
if [ -z "$NET" ]; then
|
||||
NET="24"
|
||||
NET="24"
|
||||
fi
|
||||
|
||||
/sbin/ip addr del ${IP}/${NET} dev "$1"
|
||||
|
|
|
@ -4,10 +4,8 @@ utility.py: defines miscellaneous utility services.
|
|||
|
||||
import os
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core.nodes.ipaddress import Ipv4Prefix
|
||||
from core.nodes.ipaddress import Ipv6Prefix
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix
|
||||
from core.services.coreservices import CoreService
|
||||
|
||||
|
||||
|
@ -15,6 +13,7 @@ class UtilService(CoreService):
|
|||
"""
|
||||
Parent class for utility services.
|
||||
"""
|
||||
|
||||
name = None
|
||||
group = "Utility"
|
||||
dirs = ()
|
||||
|
@ -52,12 +51,19 @@ class IPForwardService(UtilService):
|
|||
%(sysctl)s -w net.ipv4.conf.default.send_redirects=0
|
||||
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0
|
||||
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0
|
||||
""" % {'sysctl': constants.SYSCTL_BIN}
|
||||
""" % {
|
||||
"sysctl": constants.SYSCTL_BIN
|
||||
}
|
||||
for ifc in node.netifs():
|
||||
name = utils.sysctl_devname(ifc.name)
|
||||
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (constants.SYSCTL_BIN, name)
|
||||
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % \
|
||||
(constants.SYSCTL_BIN, name)
|
||||
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (
|
||||
constants.SYSCTL_BIN,
|
||||
name,
|
||||
)
|
||||
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % (
|
||||
constants.SYSCTL_BIN,
|
||||
name,
|
||||
)
|
||||
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (constants.SYSCTL_BIN, name)
|
||||
return cfg
|
||||
|
||||
|
@ -72,7 +78,7 @@ class DefaultRouteService(UtilService):
|
|||
cfg = "#!/bin/sh\n"
|
||||
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
|
||||
cfg += "\n"
|
||||
|
@ -107,7 +113,7 @@ class DefaultMulticastRouteService(UtilService):
|
|||
cfg += "as needed\n"
|
||||
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
if os.uname()[0] == "Linux":
|
||||
rtcmd = "ip route add 224.0.0.0/4 dev"
|
||||
|
@ -132,7 +138,7 @@ class StaticRouteService(UtilService):
|
|||
cfg += "# NOTE: this service must be customized to be of any use\n"
|
||||
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
|
||||
cfg += "\n"
|
||||
|
@ -158,8 +164,8 @@ class StaticRouteService(UtilService):
|
|||
|
||||
class SshService(UtilService):
|
||||
name = "SSH"
|
||||
configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
|
||||
dirs = ("/etc/ssh", "/var/run/sshd",)
|
||||
configs = ("startsshd.sh", "/etc/ssh/sshd_config")
|
||||
dirs = ("/etc/ssh", "/var/run/sshd")
|
||||
startup = ("sh startsshd.sh",)
|
||||
shutdown = ("killall sshd",)
|
||||
validate = ()
|
||||
|
@ -181,7 +187,11 @@ ssh-keygen -q -t rsa -N "" -f %s/ssh_host_rsa_key
|
|||
chmod 655 %s
|
||||
# wait until RSA host key has been generated to launch sshd
|
||||
/usr/sbin/sshd -f %s/sshd_config
|
||||
""" % (sshcfgdir, sshstatedir, sshcfgdir)
|
||||
""" % (
|
||||
sshcfgdir,
|
||||
sshstatedir,
|
||||
sshcfgdir,
|
||||
)
|
||||
else:
|
||||
return """\
|
||||
# auto-generated by SSH service (utility.py)
|
||||
|
@ -221,14 +231,18 @@ AcceptEnv LANG LC_*
|
|||
Subsystem sftp %s/sftp-server
|
||||
UsePAM yes
|
||||
UseDNS no
|
||||
""" % (sshcfgdir, sshstatedir, sshlibdir)
|
||||
""" % (
|
||||
sshcfgdir,
|
||||
sshstatedir,
|
||||
sshlibdir,
|
||||
)
|
||||
|
||||
|
||||
class DhcpService(UtilService):
|
||||
name = "DHCP"
|
||||
configs = ("/etc/dhcp/dhcpd.conf",)
|
||||
dirs = ("/etc/dhcp","/var/lib/dhcp")
|
||||
startup = ("touch /var/lib/dhcp/dhcpd.leases","dhcpd")
|
||||
dirs = ("/etc/dhcp", "/var/lib/dhcp")
|
||||
startup = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd")
|
||||
shutdown = ("killall dhcpd",)
|
||||
validate = ("pidof dhcpd",)
|
||||
|
||||
|
@ -253,7 +267,7 @@ max-lease-time 7200;
|
|||
ddns-update-style none;
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
|
||||
cfg += "\n"
|
||||
|
@ -281,13 +295,20 @@ subnet %s netmask %s {
|
|||
option routers %s;
|
||||
}
|
||||
}
|
||||
""" % (net.prefix_str(), net.netmask_str(), rangelow, rangehigh, addr)
|
||||
""" % (
|
||||
net.prefix_str(),
|
||||
net.netmask_str(),
|
||||
rangelow,
|
||||
rangehigh,
|
||||
addr,
|
||||
)
|
||||
|
||||
|
||||
class DhcpClientService(UtilService):
|
||||
"""
|
||||
Use a DHCP client for all interfaces for addressing.
|
||||
"""
|
||||
|
||||
name = "DHCPClient"
|
||||
configs = ("startdhcpclient.sh",)
|
||||
startup = ("sh startdhcpclient.sh",)
|
||||
|
@ -306,7 +327,7 @@ class DhcpClientService(UtilService):
|
|||
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
|
||||
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
|
||||
cfg += " /var/run/resolvconf/resolv.conf\n"
|
||||
|
@ -319,9 +340,10 @@ class FtpService(UtilService):
|
|||
"""
|
||||
Start a vsftpd server.
|
||||
"""
|
||||
|
||||
name = "FTP"
|
||||
configs = ("vsftpd.conf",)
|
||||
dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
|
||||
dirs = ("/var/run/vsftpd/empty", "/var/ftp")
|
||||
startup = ("vsftpd ./vsftpd.conf",)
|
||||
shutdown = ("killall vsftpd",)
|
||||
validate = ("pidof vsftpd",)
|
||||
|
@ -351,12 +373,22 @@ class HttpService(UtilService):
|
|||
"""
|
||||
Start an apache server.
|
||||
"""
|
||||
|
||||
name = "HTTP"
|
||||
configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
|
||||
"/var/www/index.html",)
|
||||
dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2",
|
||||
"/run/lock", "/var/lock/apache2", "/var/www",)
|
||||
startup = ("chown www-data /var/lock/apache2", "apache2ctl start",)
|
||||
configs = (
|
||||
"/etc/apache2/apache2.conf",
|
||||
"/etc/apache2/envvars",
|
||||
"/var/www/index.html",
|
||||
)
|
||||
dirs = (
|
||||
"/etc/apache2",
|
||||
"/var/run/apache2",
|
||||
"/var/log/apache2",
|
||||
"/run/lock",
|
||||
"/var/lock/apache2",
|
||||
"/var/www",
|
||||
)
|
||||
startup = ("chown www-data /var/lock/apache2", "apache2ctl start")
|
||||
shutdown = ("apache2ctl stop",)
|
||||
validate = ("pidof apache2",)
|
||||
|
||||
|
@ -382,38 +414,40 @@ class HttpService(UtilService):
|
|||
Detect the apache2 version using the 'a2query' command.
|
||||
"""
|
||||
try:
|
||||
status, result = utils.cmd_output(['a2query', '-v'])
|
||||
status, result = utils.cmd_output(["a2query", "-v"])
|
||||
except CoreCommandError:
|
||||
status = -1
|
||||
|
||||
if status == 0 and result[:3] == '2.4':
|
||||
if status == 0 and result[:3] == "2.4":
|
||||
return cls.APACHEVER24
|
||||
|
||||
return cls.APACHEVER22
|
||||
|
||||
@classmethod
|
||||
def generateapache2conf(cls, node, filename):
|
||||
lockstr = {cls.APACHEVER22:
|
||||
'LockFile ${APACHE_LOCK_DIR}/accept.lock\n',
|
||||
cls.APACHEVER24:
|
||||
'Mutex file:${APACHE_LOCK_DIR} default\n', }
|
||||
mpmstr = {cls.APACHEVER22: '', cls.APACHEVER24:
|
||||
'LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n', }
|
||||
lockstr = {
|
||||
cls.APACHEVER22: "LockFile ${APACHE_LOCK_DIR}/accept.lock\n",
|
||||
cls.APACHEVER24: "Mutex file:${APACHE_LOCK_DIR} default\n",
|
||||
}
|
||||
mpmstr = {
|
||||
cls.APACHEVER22: "",
|
||||
cls.APACHEVER24: "LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n",
|
||||
}
|
||||
|
||||
permstr = {cls.APACHEVER22:
|
||||
' Order allow,deny\n Deny from all\n Satisfy all\n',
|
||||
cls.APACHEVER24:
|
||||
' Require all denied\n', }
|
||||
permstr = {
|
||||
cls.APACHEVER22: " Order allow,deny\n Deny from all\n Satisfy all\n",
|
||||
cls.APACHEVER24: " Require all denied\n",
|
||||
}
|
||||
|
||||
authstr = {cls.APACHEVER22:
|
||||
'LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n',
|
||||
cls.APACHEVER24:
|
||||
'LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n', }
|
||||
authstr = {
|
||||
cls.APACHEVER22: "LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n",
|
||||
cls.APACHEVER24: "LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n",
|
||||
}
|
||||
|
||||
permstr2 = {cls.APACHEVER22:
|
||||
'\t\tOrder allow,deny\n\t\tallow from all\n',
|
||||
cls.APACHEVER24:
|
||||
'\t\tRequire all granted\n', }
|
||||
permstr2 = {
|
||||
cls.APACHEVER22: "\t\tOrder allow,deny\n\t\tallow from all\n",
|
||||
cls.APACHEVER24: "\t\tRequire all granted\n",
|
||||
}
|
||||
|
||||
version = cls.detectversionfromcmd()
|
||||
cfg = "# apache2.conf generated by utility.py:HttpService\n"
|
||||
|
@ -461,7 +495,7 @@ Group ${APACHE_RUN_GROUP}
|
|||
|
||||
AccessFileName .htaccess
|
||||
|
||||
<Files ~ "^\.ht">
|
||||
<Files ~ "^\\.ht">
|
||||
"""
|
||||
cfg += permstr[version]
|
||||
cfg += """\
|
||||
|
@ -508,22 +542,22 @@ ServerSignature On
|
|||
TraceEnable Off
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
<Directory /var/www/>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride None
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
<Directory /var/www/>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride None
|
||||
"""
|
||||
cfg += permstr2[version]
|
||||
cfg += """\
|
||||
</Directory>
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
LogLevel warn
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
</Directory>
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
LogLevel warn
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
</VirtualHost>
|
||||
|
||||
"""
|
||||
|
@ -546,14 +580,17 @@ export LANG
|
|||
|
||||
@classmethod
|
||||
def generatehtml(cls, node, filename):
|
||||
body = """\
|
||||
body = (
|
||||
"""\
|
||||
<!-- generated by utility.py:HttpService -->
|
||||
<h1>%s web server</h1>
|
||||
<p>This is the default web page for this server.</p>
|
||||
<p>The web server software is running but no content has been added, yet.</p>
|
||||
""" % node.name
|
||||
"""
|
||||
% node.name
|
||||
)
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
||||
return "<html><body>%s</body></html>" % body
|
||||
|
@ -563,6 +600,7 @@ class PcapService(UtilService):
|
|||
"""
|
||||
Pcap service for logging packets.
|
||||
"""
|
||||
|
||||
name = "pcap"
|
||||
configs = ("pcap.sh",)
|
||||
dirs = ()
|
||||
|
@ -586,11 +624,15 @@ if [ "x$1" = "xstart" ]; then
|
|||
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
cfg += '# '
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
cfg += "# "
|
||||
redir = "< /dev/null"
|
||||
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
|
||||
(node.name, ifc.name, ifc.name, redir)
|
||||
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % (
|
||||
node.name,
|
||||
ifc.name,
|
||||
ifc.name,
|
||||
redir,
|
||||
)
|
||||
cfg += """
|
||||
|
||||
elif [ "x$1" = "xstop" ]; then
|
||||
|
@ -617,12 +659,13 @@ class RadvdService(UtilService):
|
|||
"""
|
||||
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
prefixes = map(cls.subnetentry, ifc.addrlist)
|
||||
if len(prefixes) < 1:
|
||||
continue
|
||||
cfg += """\
|
||||
cfg += (
|
||||
"""\
|
||||
interface %s
|
||||
{
|
||||
AdvSendAdvert on;
|
||||
|
@ -630,18 +673,23 @@ interface %s
|
|||
MaxRtrAdvInterval 10;
|
||||
AdvDefaultPreference low;
|
||||
AdvHomeAgentFlag off;
|
||||
""" % ifc.name
|
||||
"""
|
||||
% ifc.name
|
||||
)
|
||||
for prefix in prefixes:
|
||||
if prefix == "":
|
||||
continue
|
||||
cfg += """\
|
||||
cfg += (
|
||||
"""\
|
||||
prefix %s
|
||||
{
|
||||
AdvOnLink on;
|
||||
AdvAutonomous on;
|
||||
AdvRouterAddr on;
|
||||
};
|
||||
""" % prefix
|
||||
"""
|
||||
% prefix
|
||||
)
|
||||
cfg += "};\n"
|
||||
return cfg
|
||||
|
||||
|
@ -662,6 +710,7 @@ class AtdService(UtilService):
|
|||
"""
|
||||
Atd service for scheduling at jobs
|
||||
"""
|
||||
|
||||
name = "atd"
|
||||
configs = ("startatd.sh",)
|
||||
dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
||||
|
@ -683,5 +732,6 @@ class UserDefinedService(UtilService):
|
|||
"""
|
||||
Dummy service allowing customization of anything.
|
||||
"""
|
||||
|
||||
name = "UserDefined"
|
||||
meta = "Customize this service to do anything upon startup."
|
||||
|
|
|
@ -12,12 +12,16 @@ class XorpRtrmgr(CoreService):
|
|||
XORP router manager service builds a config.boot file based on other
|
||||
enabled XORP services, and launches necessary daemons upon startup.
|
||||
"""
|
||||
|
||||
name = "xorp_rtrmgr"
|
||||
executables = ("xorp_rtrmgr",)
|
||||
group = "XORP"
|
||||
dirs = ("/etc/xorp",)
|
||||
configs = ("/etc/xorp/config.boot",)
|
||||
startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (configs[0], name, name),)
|
||||
startup = (
|
||||
"xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid"
|
||||
% (configs[0], name, name),
|
||||
)
|
||||
shutdown = ("killall xorp_rtrmgr",)
|
||||
validate = ("pidof xorp_rtrmgr",)
|
||||
|
||||
|
@ -74,6 +78,7 @@ class XorpService(CoreService):
|
|||
Parent class for XORP services. Defines properties and methods
|
||||
common to XORP's routing daemons.
|
||||
"""
|
||||
|
||||
name = None
|
||||
executables = ("xorp_rtrmgr",)
|
||||
group = "XORP"
|
||||
|
@ -103,7 +108,7 @@ class XorpService(CoreService):
|
|||
"""
|
||||
names = []
|
||||
for ifc in ifcs:
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
names.append(ifc.name)
|
||||
names.append("register_vif")
|
||||
|
@ -129,7 +134,7 @@ class XorpService(CoreService):
|
|||
cfg += " policy-statement export-connected {\n"
|
||||
cfg += "\tterm 100 {\n"
|
||||
cfg += "\t from {\n"
|
||||
cfg += "\t\tprotocol: \"connected\"\n"
|
||||
cfg += '\t\tprotocol: "connected"\n'
|
||||
cfg += "\t }\n"
|
||||
cfg += "\t}\n"
|
||||
cfg += " }\n"
|
||||
|
@ -142,11 +147,11 @@ class XorpService(CoreService):
|
|||
Helper to return the first IPv4 address of a node as its router ID.
|
||||
"""
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
for a in ifc.addrlist:
|
||||
if a.find(".") >= 0:
|
||||
return a.split('/')[0]
|
||||
return a.split("/")[0]
|
||||
# raise ValueError, "no IPv4 address found for router ID"
|
||||
return "0.0.0.0"
|
||||
|
||||
|
@ -165,6 +170,7 @@ class XorpOspfv2(XorpService):
|
|||
not build its own configuration file but has hooks for adding to the
|
||||
unified XORP configuration file.
|
||||
"""
|
||||
|
||||
name = "XORP_OSPFv2"
|
||||
|
||||
@classmethod
|
||||
|
@ -176,7 +182,7 @@ class XorpOspfv2(XorpService):
|
|||
cfg += "\trouter-id: %s\n" % rtrid
|
||||
cfg += "\tarea 0.0.0.0 {\n"
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\t interface %s {\n" % ifc.name
|
||||
cfg += "\t\tvif %s {\n" % ifc.name
|
||||
|
@ -200,6 +206,7 @@ class XorpOspfv3(XorpService):
|
|||
not build its own configuration file but has hooks for adding to the
|
||||
unified XORP configuration file.
|
||||
"""
|
||||
|
||||
name = "XORP_OSPFv3"
|
||||
|
||||
@classmethod
|
||||
|
@ -211,7 +218,7 @@ class XorpOspfv3(XorpService):
|
|||
cfg += "\trouter-id: %s\n" % rtrid
|
||||
cfg += "\tarea 0.0.0.0 {\n"
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\t interface %s {\n" % ifc.name
|
||||
cfg += "\t\tvif %s {\n" % ifc.name
|
||||
|
@ -227,6 +234,7 @@ class XorpBgp(XorpService):
|
|||
"""
|
||||
IPv4 inter-domain routing. AS numbers and peers must be customized.
|
||||
"""
|
||||
|
||||
name = "XORP_BGP"
|
||||
custom_needed = True
|
||||
|
||||
|
@ -241,7 +249,7 @@ class XorpBgp(XorpService):
|
|||
cfg += " bgp {\n"
|
||||
cfg += "\tbgp-id: %s\n" % rtrid
|
||||
cfg += "\tlocal-as: 65001 /* change this */\n"
|
||||
cfg += "\texport: \"export-connected\"\n"
|
||||
cfg += '\texport: "export-connected"\n'
|
||||
cfg += "\tpeer 10.0.1.1 { /* change this */\n"
|
||||
cfg += "\t local-ip: 10.0.1.1\n"
|
||||
cfg += "\t as: 65002\n"
|
||||
|
@ -265,9 +273,9 @@ class XorpRip(XorpService):
|
|||
cfg += cls.policyexportconnected()
|
||||
cfg += "\nprotocols {\n"
|
||||
cfg += " rip {\n"
|
||||
cfg += "\texport: \"export-connected\"\n"
|
||||
cfg += '\texport: "export-connected"\n'
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\tinterface %s {\n" % ifc.name
|
||||
cfg += "\t vif %s {\n" % ifc.name
|
||||
|
@ -289,6 +297,7 @@ class XorpRipng(XorpService):
|
|||
"""
|
||||
RIP NG IPv6 unicast routing.
|
||||
"""
|
||||
|
||||
name = "XORP_RIPNG"
|
||||
|
||||
@classmethod
|
||||
|
@ -297,9 +306,9 @@ class XorpRipng(XorpService):
|
|||
cfg += cls.policyexportconnected()
|
||||
cfg += "\nprotocols {\n"
|
||||
cfg += " ripng {\n"
|
||||
cfg += "\texport: \"export-connected\"\n"
|
||||
cfg += '\texport: "export-connected"\n'
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\tinterface %s {\n" % ifc.name
|
||||
cfg += "\t vif %s {\n" % ifc.name
|
||||
|
@ -324,6 +333,7 @@ class XorpPimSm4(XorpService):
|
|||
"""
|
||||
PIM Sparse Mode IPv4 multicast routing.
|
||||
"""
|
||||
|
||||
name = "XORP_PIMSM4"
|
||||
|
||||
@classmethod
|
||||
|
@ -334,7 +344,7 @@ class XorpPimSm4(XorpService):
|
|||
cfg += " igmp {\n"
|
||||
names = []
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
names.append(ifc.name)
|
||||
cfg += "\tinterface %s {\n" % ifc.name
|
||||
|
@ -358,12 +368,12 @@ class XorpPimSm4(XorpService):
|
|||
cfg += "\tbootstrap {\n"
|
||||
cfg += "\t cand-bsr {\n"
|
||||
cfg += "\t\tscope-zone 224.0.0.0/4 {\n"
|
||||
cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0]
|
||||
cfg += '\t\t cand-bsr-by-vif-name: "%s"\n' % names[0]
|
||||
cfg += "\t\t}\n"
|
||||
cfg += "\t }\n"
|
||||
cfg += "\t cand-rp {\n"
|
||||
cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n"
|
||||
cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0]
|
||||
cfg += '\t\t cand-rp-by-vif-name: "%s"\n' % names[0]
|
||||
cfg += "\t\t}\n"
|
||||
cfg += "\t }\n"
|
||||
cfg += "\t}\n"
|
||||
|
@ -383,6 +393,7 @@ class XorpPimSm6(XorpService):
|
|||
"""
|
||||
PIM Sparse Mode IPv6 multicast routing.
|
||||
"""
|
||||
|
||||
name = "XORP_PIMSM6"
|
||||
|
||||
@classmethod
|
||||
|
@ -393,7 +404,7 @@ class XorpPimSm6(XorpService):
|
|||
cfg += " mld {\n"
|
||||
names = []
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
names.append(ifc.name)
|
||||
cfg += "\tinterface %s {\n" % ifc.name
|
||||
|
@ -417,12 +428,12 @@ class XorpPimSm6(XorpService):
|
|||
cfg += "\tbootstrap {\n"
|
||||
cfg += "\t cand-bsr {\n"
|
||||
cfg += "\t\tscope-zone ff00::/8 {\n"
|
||||
cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0]
|
||||
cfg += '\t\t cand-bsr-by-vif-name: "%s"\n' % names[0]
|
||||
cfg += "\t\t}\n"
|
||||
cfg += "\t }\n"
|
||||
cfg += "\t cand-rp {\n"
|
||||
cfg += "\t\tgroup-prefix ff00::/8 {\n"
|
||||
cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0]
|
||||
cfg += '\t\t cand-rp-by-vif-name: "%s"\n' % names[0]
|
||||
cfg += "\t\t}\n"
|
||||
cfg += "\t }\n"
|
||||
cfg += "\t}\n"
|
||||
|
@ -442,6 +453,7 @@ class XorpOlsr(XorpService):
|
|||
"""
|
||||
OLSR IPv4 unicast MANET routing.
|
||||
"""
|
||||
|
||||
name = "XORP_OLSR"
|
||||
|
||||
@classmethod
|
||||
|
@ -452,7 +464,7 @@ class XorpOlsr(XorpService):
|
|||
cfg += " olsr4 {\n"
|
||||
cfg += "\tmain-address: %s\n" % rtrid
|
||||
for ifc in node.netifs():
|
||||
if hasattr(ifc, 'control') and ifc.control is True:
|
||||
if hasattr(ifc, "control") and ifc.control is True:
|
||||
continue
|
||||
cfg += "\tinterface %s {\n" % ifc.name
|
||||
cfg += "\t vif %s {\n" % ifc.name
|
||||
|
|
|
@ -11,6 +11,7 @@ import os
|
|||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from past.builtins import basestring
|
||||
|
||||
from core import CoreCommandError
|
||||
|
@ -30,10 +31,7 @@ def execute_file(path, exec_globals=None, exec_locals=None):
|
|||
"""
|
||||
if exec_globals is None:
|
||||
exec_globals = {}
|
||||
exec_globals.update({
|
||||
"__file__": path,
|
||||
"__name__": "__main__"
|
||||
})
|
||||
exec_globals.update({"__file__": path, "__name__": "__main__"})
|
||||
with open(path, "rb") as f:
|
||||
data = compile(f.read(), path, "exec")
|
||||
exec(data, exec_globals, exec_locals)
|
||||
|
@ -157,7 +155,7 @@ def make_tuple(obj):
|
|||
if hasattr(obj, "__iter__"):
|
||||
return tuple(obj)
|
||||
else:
|
||||
return obj,
|
||||
return (obj,)
|
||||
|
||||
|
||||
def make_tuple_fromstr(s, value_type):
|
||||
|
@ -290,7 +288,10 @@ def hex_dump(s, bytes_per_word=2, words_per_line=8):
|
|||
while s:
|
||||
line = s[:total_bytes]
|
||||
s = s[total_bytes:]
|
||||
tmp = map(lambda x: ("%02x" * bytes_per_word) % x, zip(*[iter(map(ord, line))] * bytes_per_word))
|
||||
tmp = map(
|
||||
lambda x: ("%02x" * bytes_per_word) % x,
|
||||
zip(*[iter(map(ord, line))] * bytes_per_word),
|
||||
)
|
||||
if len(line) % 2:
|
||||
tmp.append("%x" % ord(line[-1]))
|
||||
dump += "0x%08x: %s\n" % (count, " ".join(tmp))
|
||||
|
@ -437,7 +438,9 @@ def load_classes(path, clazz):
|
|||
for member in members:
|
||||
valid_class = member[1]
|
||||
classes.append(valid_class)
|
||||
except:
|
||||
logging.exception("unexpected error during import, skipping: %s", import_statement)
|
||||
except Exception:
|
||||
logging.exception(
|
||||
"unexpected error during import, skipping: %s", import_statement
|
||||
)
|
||||
|
||||
return classes
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
import logging
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
|
||||
from lxml import etree
|
||||
|
||||
import core.nodes.base
|
||||
import core.nodes.physical
|
||||
from core.emulator.emudata import InterfaceData
|
||||
from core.emulator.emudata import LinkOptions
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
|
||||
|
||||
def write_xml_file(xml_element, file_path, doctype=None):
|
||||
xml_data = etree.tostring(xml_element, xml_declaration=True, pretty_print=True, encoding="UTF-8", doctype=doctype)
|
||||
xml_data = etree.tostring(
|
||||
xml_element,
|
||||
xml_declaration=True,
|
||||
pretty_print=True,
|
||||
encoding="UTF-8",
|
||||
doctype=doctype,
|
||||
)
|
||||
with open(file_path, "wb") as xml_file:
|
||||
xml_file.write(xml_data)
|
||||
|
||||
|
@ -252,7 +257,9 @@ class CoreXmlWriter(object):
|
|||
|
||||
# write out generated xml
|
||||
xml_tree = etree.ElementTree(self.scenario)
|
||||
xml_tree.write(file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8")
|
||||
xml_tree.write(
|
||||
file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8"
|
||||
)
|
||||
|
||||
def write_session_origin(self):
|
||||
# origin: geolocation of cartesian coordinate 0,0,0
|
||||
|
@ -327,12 +334,18 @@ class CoreXmlWriter(object):
|
|||
|
||||
for model_name in all_configs:
|
||||
config = all_configs[model_name]
|
||||
logging.info("writing emane config node(%s) model(%s)", node_id, model_name)
|
||||
logging.info(
|
||||
"writing emane config node(%s) model(%s)", node_id, model_name
|
||||
)
|
||||
if model_name == -1:
|
||||
emane_configuration = create_emane_config(node_id, self.session.emane.emane_config, config)
|
||||
emane_configuration = create_emane_config(
|
||||
node_id, self.session.emane.emane_config, config
|
||||
)
|
||||
else:
|
||||
model = self.session.emane.models[model_name]
|
||||
emane_configuration = create_emane_model_config(node_id, model, config)
|
||||
emane_configuration = create_emane_model_config(
|
||||
node_id, model, config
|
||||
)
|
||||
emane_configurations.append(emane_configuration)
|
||||
|
||||
if emane_configurations.getchildren():
|
||||
|
@ -347,8 +360,12 @@ class CoreXmlWriter(object):
|
|||
|
||||
for model_name in all_configs:
|
||||
config = all_configs[model_name]
|
||||
logging.info("writing mobility config node(%s) model(%s)", node_id, model_name)
|
||||
mobility_configuration = etree.SubElement(mobility_configurations, "mobility_configuration")
|
||||
logging.debug(
|
||||
"writing mobility config node(%s) model(%s)", node_id, model_name
|
||||
)
|
||||
mobility_configuration = etree.SubElement(
|
||||
mobility_configurations, "mobility_configuration"
|
||||
)
|
||||
add_attribute(mobility_configuration, "node", node_id)
|
||||
add_attribute(mobility_configuration, "model", model_name)
|
||||
for name in config:
|
||||
|
@ -388,7 +405,9 @@ class CoreXmlWriter(object):
|
|||
for node_id in self.session.nodes:
|
||||
node = self.session.nodes[node_id]
|
||||
# network node
|
||||
is_network_or_rj45 = isinstance(node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node))
|
||||
is_network_or_rj45 = isinstance(
|
||||
node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node)
|
||||
)
|
||||
is_controlnet = nodeutils.is_node(node, NodeTypes.CONTROL_NET)
|
||||
if is_network_or_rj45 and not is_controlnet:
|
||||
self.write_network(node)
|
||||
|
@ -430,7 +449,9 @@ class CoreXmlWriter(object):
|
|||
device = DeviceElement(self.session, node)
|
||||
self.devices.append(device.element)
|
||||
|
||||
def create_interface_element(self, element_name, node_id, interface_id, mac, ip4, ip4_mask, ip6, ip6_mask):
|
||||
def create_interface_element(
|
||||
self, element_name, node_id, interface_id, mac, ip4, ip4_mask, ip6, ip6_mask
|
||||
):
|
||||
interface = etree.Element(element_name)
|
||||
node = self.session.get_node(node_id)
|
||||
interface_name = None
|
||||
|
@ -468,7 +489,7 @@ class CoreXmlWriter(object):
|
|||
link_data.interface1_ip4,
|
||||
link_data.interface1_ip4_mask,
|
||||
link_data.interface1_ip6,
|
||||
link_data.interface1_ip6_mask
|
||||
link_data.interface1_ip6_mask,
|
||||
)
|
||||
link_element.append(interface_one)
|
||||
|
||||
|
@ -482,7 +503,7 @@ class CoreXmlWriter(object):
|
|||
link_data.interface2_ip4,
|
||||
link_data.interface2_ip4_mask,
|
||||
link_data.interface2_ip6,
|
||||
link_data.interface2_ip6_mask
|
||||
link_data.interface2_ip6_mask,
|
||||
)
|
||||
link_element.append(interface_two)
|
||||
|
||||
|
@ -541,7 +562,9 @@ class CoreXmlReader(object):
|
|||
services = []
|
||||
for service in node.iterchildren():
|
||||
services.append(service.get("name"))
|
||||
logging.info("reading default services for nodes(%s): %s", node_type, services)
|
||||
logging.info(
|
||||
"reading default services for nodes(%s): %s", node_type, services
|
||||
)
|
||||
self.session.services.default_services[node_type] = services
|
||||
|
||||
def read_session_metadata(self):
|
||||
|
@ -581,7 +604,9 @@ class CoreXmlReader(object):
|
|||
data = hook.text
|
||||
hook_type = "hook:%s" % state
|
||||
logging.info("reading hook: state(%s) name(%s)", state, name)
|
||||
self.session.set_hook(hook_type, file_name=name, source_name=None, data=data)
|
||||
self.session.set_hook(
|
||||
hook_type, file_name=name, source_name=None, data=data
|
||||
)
|
||||
|
||||
def read_session_origin(self):
|
||||
session_origin = self.scenario.find("session_origin")
|
||||
|
@ -615,7 +640,9 @@ class CoreXmlReader(object):
|
|||
for service_configuration in service_configurations.iterchildren():
|
||||
node_id = get_int(service_configuration, "node")
|
||||
service_name = service_configuration.get("name")
|
||||
logging.info("reading custom service(%s) for node(%s)", service_name, node_id)
|
||||
logging.info(
|
||||
"reading custom service(%s) for node(%s)", service_name, node_id
|
||||
)
|
||||
self.session.services.set_service(node_id, service_name)
|
||||
service = self.session.services.get_service(node_id, service_name)
|
||||
|
||||
|
@ -629,11 +656,15 @@ class CoreXmlReader(object):
|
|||
|
||||
validate_elements = service_configuration.find("validates")
|
||||
if validate_elements is not None:
|
||||
service.validate = tuple(x.text for x in validate_elements.iterchildren())
|
||||
service.validate = tuple(
|
||||
x.text for x in validate_elements.iterchildren()
|
||||
)
|
||||
|
||||
shutdown_elements = service_configuration.find("shutdowns")
|
||||
if shutdown_elements is not None:
|
||||
service.shutdown = tuple(x.text for x in shutdown_elements.iterchildren())
|
||||
service.shutdown = tuple(
|
||||
x.text for x in shutdown_elements.iterchildren()
|
||||
)
|
||||
|
||||
file_elements = service_configuration.find("files")
|
||||
if file_elements is not None:
|
||||
|
@ -670,7 +701,9 @@ class CoreXmlReader(object):
|
|||
value = config.get("value")
|
||||
configs[name] = value
|
||||
|
||||
logging.info("reading emane configuration node(%s) model(%s)", node_id, model_name)
|
||||
logging.info(
|
||||
"reading emane configuration node(%s) model(%s)", node_id, model_name
|
||||
)
|
||||
self.session.emane.set_model_config(node_id, model_name, configs)
|
||||
|
||||
def read_mobility_configs(self):
|
||||
|
@ -688,7 +721,9 @@ class CoreXmlReader(object):
|
|||
value = config.get("value")
|
||||
configs[name] = value
|
||||
|
||||
logging.info("reading mobility configuration node(%s) model(%s)", node_id, model_name)
|
||||
logging.info(
|
||||
"reading mobility configuration node(%s) model(%s)", node_id, model_name
|
||||
)
|
||||
self.session.mobility.set_model_config(node_id, model_name, configs)
|
||||
|
||||
def read_nodes(self):
|
||||
|
@ -710,7 +745,9 @@ class CoreXmlReader(object):
|
|||
|
||||
service_elements = device_element.find("services")
|
||||
if service_elements is not None:
|
||||
node_options.services = [x.get("name") for x in service_elements.iterchildren()]
|
||||
node_options.services = [
|
||||
x.get("name") for x in service_elements.iterchildren()
|
||||
]
|
||||
|
||||
position_element = device_element.find("position")
|
||||
if position_element is not None:
|
||||
|
@ -747,7 +784,9 @@ class CoreXmlReader(object):
|
|||
if all([lat, lon, alt]):
|
||||
node_options.set_location(lat, lon, alt)
|
||||
|
||||
logging.info("reading node id(%s) node_type(%s) name(%s)", node_id, node_type, name)
|
||||
logging.info(
|
||||
"reading node id(%s) node_type(%s) name(%s)", node_id, node_type, name
|
||||
)
|
||||
self.session.add_node(_type=node_type, _id=node_id, node_options=node_options)
|
||||
|
||||
def read_links(self):
|
||||
|
@ -791,10 +830,24 @@ class CoreXmlReader(object):
|
|||
link_options.gui_attributes = options_element.get("gui_attributes")
|
||||
|
||||
if link_options.unidirectional == 1 and node_set in node_sets:
|
||||
logging.info("updating link node_one(%s) node_two(%s): %s", node_one, node_two, link_options)
|
||||
self.session.update_link(node_one, node_two, interface_one.id, interface_two.id, link_options)
|
||||
logging.info(
|
||||
"updating link node_one(%s) node_two(%s): %s",
|
||||
node_one,
|
||||
node_two,
|
||||
link_options,
|
||||
)
|
||||
self.session.update_link(
|
||||
node_one, node_two, interface_one.id, interface_two.id, link_options
|
||||
)
|
||||
else:
|
||||
logging.info("adding link node_one(%s) node_two(%s): %s", node_one, node_two, link_options)
|
||||
self.session.add_link(node_one, node_two, interface_one, interface_two, link_options)
|
||||
logging.info(
|
||||
"adding link node_one(%s) node_two(%s): %s",
|
||||
node_one,
|
||||
node_two,
|
||||
link_options,
|
||||
)
|
||||
self.session.add_link(
|
||||
node_one, node_two, interface_one, interface_two, link_options
|
||||
)
|
||||
|
||||
node_sets.add(node_set)
|
||||
|
|
|
@ -4,9 +4,9 @@ import socket
|
|||
from lxml import etree
|
||||
|
||||
from core import constants, utils
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import nodeutils, ipaddress
|
||||
from core.nodes import ipaddress, nodeutils
|
||||
from core.nodes.base import CoreNodeBase
|
||||
|
||||
|
||||
def add_type(parent_element, name):
|
||||
|
@ -31,16 +31,22 @@ def add_emane_interface(host_element, netif, platform_name="p1", transport_name=
|
|||
|
||||
# platform data
|
||||
platform_id = "%s/%s" % (host_id, platform_name)
|
||||
platform_element = etree.SubElement(host_element, "emanePlatform", id=platform_id, name=platform_name)
|
||||
platform_element = etree.SubElement(
|
||||
host_element, "emanePlatform", id=platform_id, name=platform_name
|
||||
)
|
||||
|
||||
# transport data
|
||||
transport_id = "%s/%s" % (host_id, transport_name)
|
||||
etree.SubElement(platform_element, "transport", id=transport_id, name=transport_name)
|
||||
etree.SubElement(
|
||||
platform_element, "transport", id=transport_id, name=transport_name
|
||||
)
|
||||
|
||||
# nem data
|
||||
nem_name = "nem%s" % nem_id
|
||||
nem_element_id = "%s/%s" % (host_id, nem_name)
|
||||
nem_element = etree.SubElement(platform_element, "nem", id=nem_element_id, name=nem_name)
|
||||
nem_element = etree.SubElement(
|
||||
platform_element, "nem", id=nem_element_id, name=nem_name
|
||||
)
|
||||
nem_id_element = etree.SubElement(nem_element, "parameter", name="nemid")
|
||||
nem_id_element.text = str(nem_id)
|
||||
|
||||
|
@ -81,7 +87,9 @@ class CoreXmlDeployment(object):
|
|||
def __init__(self, session, scenario):
|
||||
self.session = session
|
||||
self.scenario = scenario
|
||||
self.root = etree.SubElement(scenario, "container", id="TestBed", name="TestBed")
|
||||
self.root = etree.SubElement(
|
||||
scenario, "container", id="TestBed", name="TestBed"
|
||||
)
|
||||
self.add_deployment()
|
||||
|
||||
def find_device(self, name):
|
||||
|
@ -89,8 +97,10 @@ class CoreXmlDeployment(object):
|
|||
return device
|
||||
|
||||
def find_interface(self, device, name):
|
||||
interface = self.scenario.find("devices/device[@name='%s']/interfaces/interface[@name='%s']" % (
|
||||
device.name, name))
|
||||
interface = self.scenario.find(
|
||||
"devices/device[@name='%s']/interfaces/interface[@name='%s']"
|
||||
% (device.name, name)
|
||||
)
|
||||
return interface
|
||||
|
||||
def add_deployment(self):
|
||||
|
@ -125,7 +135,9 @@ class CoreXmlDeployment(object):
|
|||
|
||||
# create virtual host element
|
||||
host_id = "%s/%s" % (physical_host.get("id"), node.name)
|
||||
host_element = etree.SubElement(physical_host, "testHost", id=host_id, name=node.name)
|
||||
host_element = etree.SubElement(
|
||||
physical_host, "testHost", id=host_id, name=node.name
|
||||
)
|
||||
|
||||
# add host type
|
||||
add_type(host_element, "virtual")
|
||||
|
|
|
@ -53,7 +53,10 @@ def create_file(xml_element, doc_name, file_path):
|
|||
:param str file_path: file path to write xml file to
|
||||
:return: nothing
|
||||
"""
|
||||
doctype = '<!DOCTYPE %(doc_name)s SYSTEM "file:///usr/share/emane/dtd/%(doc_name)s.dtd">' % {"doc_name": doc_name}
|
||||
doctype = (
|
||||
'<!DOCTYPE %(doc_name)s SYSTEM "file:///usr/share/emane/dtd/%(doc_name)s.dtd">'
|
||||
% {"doc_name": doc_name}
|
||||
)
|
||||
corexml.write_xml_file(xml_element, file_path, doctype=doctype)
|
||||
|
||||
|
||||
|
@ -108,7 +111,12 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
|||
:return: the next nem id that can be used for creating platform xml files
|
||||
:rtype: int
|
||||
"""
|
||||
logging.debug("building emane platform xml for node(%s) nem_id(%s): %s", node, nem_id, node.name)
|
||||
logging.debug(
|
||||
"building emane platform xml for node(%s) nem_id(%s): %s",
|
||||
node,
|
||||
nem_id,
|
||||
node.name,
|
||||
)
|
||||
nem_entries = {}
|
||||
|
||||
if node.model is None:
|
||||
|
@ -116,10 +124,14 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
|||
return nem_entries
|
||||
|
||||
for netif in node.netifs():
|
||||
logging.debug("building platform xml for interface(%s) nem_id(%s)", netif.name, nem_id)
|
||||
logging.debug(
|
||||
"building platform xml for interface(%s) nem_id(%s)", netif.name, nem_id
|
||||
)
|
||||
# build nem xml
|
||||
nem_definition = nem_file_name(node.model, netif)
|
||||
nem_element = etree.Element("nem", id=str(nem_id), name=netif.localname, definition=nem_definition)
|
||||
nem_element = etree.Element(
|
||||
"nem", id=str(nem_id), name=netif.localname, definition=nem_definition
|
||||
)
|
||||
|
||||
# check if this is an external transport, get default config if an interface specific one does not exist
|
||||
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
||||
|
@ -137,7 +149,9 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
|||
logging.info("warning: %s interface type unsupported!", netif.name)
|
||||
transport_type = "raw"
|
||||
transport_file = transport_file_name(node.id, transport_type)
|
||||
transport_element = etree.SubElement(nem_element, "transport", definition=transport_file)
|
||||
transport_element = etree.SubElement(
|
||||
nem_element, "transport", definition=transport_file
|
||||
)
|
||||
|
||||
# add transport parameter
|
||||
add_param(transport_element, "device", netif.name)
|
||||
|
@ -261,7 +275,7 @@ def build_transport_xml(emane_manager, node, transport_type):
|
|||
transport_element = etree.Element(
|
||||
"transport",
|
||||
name="%s Transport" % transport_type.capitalize(),
|
||||
library="trans%s" % transport_type.lower()
|
||||
library="trans%s" % transport_type.lower(),
|
||||
)
|
||||
|
||||
# add bitrate
|
||||
|
@ -299,7 +313,9 @@ def create_phy_xml(emane_model, config, file_path):
|
|||
if emane_model.phy_library:
|
||||
phy_element.set("library", emane_model.phy_library)
|
||||
|
||||
add_configurations(phy_element, emane_model.phy_config, config, emane_model.config_ignore)
|
||||
add_configurations(
|
||||
phy_element, emane_model.phy_config, config, emane_model.config_ignore
|
||||
)
|
||||
create_file(phy_element, "phy", file_path)
|
||||
|
||||
|
||||
|
@ -315,12 +331,18 @@ def create_mac_xml(emane_model, config, file_path):
|
|||
if not emane_model.mac_library:
|
||||
raise ValueError("must define emane model library")
|
||||
|
||||
mac_element = etree.Element("mac", name="%s MAC" % emane_model.name, library=emane_model.mac_library)
|
||||
add_configurations(mac_element, emane_model.mac_config, config, emane_model.config_ignore)
|
||||
mac_element = etree.Element(
|
||||
"mac", name="%s MAC" % emane_model.name, library=emane_model.mac_library
|
||||
)
|
||||
add_configurations(
|
||||
mac_element, emane_model.mac_config, config, emane_model.config_ignore
|
||||
)
|
||||
create_file(mac_element, "mac", file_path)
|
||||
|
||||
|
||||
def create_nem_xml(emane_model, config, nem_file, transport_definition, mac_definition, phy_definition):
|
||||
def create_nem_xml(
|
||||
emane_model, config, nem_file, transport_definition, mac_definition, phy_definition
|
||||
):
|
||||
"""
|
||||
Create the nem xml document.
|
||||
|
||||
|
@ -353,7 +375,13 @@ def create_event_service_xml(group, port, device, file_directory):
|
|||
:return: nothing
|
||||
"""
|
||||
event_element = etree.Element("emaneeventmsgsvc")
|
||||
for name, value in (("group", group), ("port", port), ("device", device), ("mcloop", "1"), ("ttl", "32")):
|
||||
for name, value in (
|
||||
("group", group),
|
||||
("port", port),
|
||||
("device", device),
|
||||
("mcloop", "1"),
|
||||
("ttl", "32"),
|
||||
):
|
||||
sub_element = etree.SubElement(event_element, name)
|
||||
sub_element.text = value
|
||||
file_name = "libemaneeventservice.xml"
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg network.
|
||||
|
||||
import datetime
|
||||
import parser
|
||||
from builtins import range
|
||||
|
||||
import parser
|
||||
from core import load_logging_config
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
|
@ -28,8 +28,7 @@ def example(options):
|
|||
|
||||
# create emane network node
|
||||
emane_network = session.create_emane_network(
|
||||
model=EmaneIeee80211abgModel,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
)
|
||||
emane_network.setposition(x=80, y=50)
|
||||
|
||||
|
@ -48,14 +47,17 @@ def example(options):
|
|||
node.client.term("bash")
|
||||
|
||||
# shutdown session
|
||||
raw_input("press enter to exit...")
|
||||
input("press enter to exit...")
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
def main():
|
||||
options = parser.parse_options("emane80211")
|
||||
start = datetime.datetime.now()
|
||||
print("running emane 80211 example: nodes(%s) time(%s)" % (options.nodes, options.time))
|
||||
print(
|
||||
"running emane 80211 example: nodes(%s) time(%s)"
|
||||
% (options.nodes, options.time)
|
||||
)
|
||||
example(options)
|
||||
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
||||
|
||||
|
|
|
@ -7,10 +7,20 @@ DEFAULT_STEP = 1
|
|||
|
||||
def parse_options(name):
|
||||
parser = argparse.ArgumentParser(description="Run %s example" % name)
|
||||
parser.add_argument("-n", "--nodes", type=int, default=DEFAULT_NODES,
|
||||
help="number of nodes to create in this example")
|
||||
parser.add_argument("-t", "--time", type=int, default=DEFAULT_TIME,
|
||||
help="example iperf run time in seconds")
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--nodes",
|
||||
type=int,
|
||||
default=DEFAULT_NODES,
|
||||
help="number of nodes to create in this example",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--time",
|
||||
type=int,
|
||||
default=DEFAULT_TIME,
|
||||
help="example iperf run time in seconds",
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
# nodestep
|
||||
|
||||
import datetime
|
||||
import parser
|
||||
from builtins import range
|
||||
|
||||
import parser
|
||||
from core import load_logging_config
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
load_logging_config()
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ from builtins import range
|
|||
|
||||
from core import load_logging_config
|
||||
from core.emulator.emudata import IpPrefixes
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
load_logging_config()
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
# nodestep
|
||||
|
||||
import datetime
|
||||
import parser
|
||||
from builtins import range
|
||||
|
||||
import parser
|
||||
from core import load_logging_config
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
|
||||
load_logging_config()
|
||||
|
|
|
@ -2,8 +2,7 @@ import logging
|
|||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
|
|
@ -2,8 +2,7 @@ import logging
|
|||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
|
|
@ -2,8 +2,7 @@ import logging
|
|||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
|
|
@ -21,7 +21,9 @@ def main():
|
|||
core.events(session_id, log_event)
|
||||
|
||||
# change session state
|
||||
response = core.set_session_state(session_id, core_pb2.SessionState.CONFIGURATION)
|
||||
response = core.set_session_state(
|
||||
session_id, core_pb2.SessionState.CONFIGURATION
|
||||
)
|
||||
logging.info("set session state: %s", response)
|
||||
|
||||
# create switch node
|
||||
|
@ -47,7 +49,9 @@ def main():
|
|||
logging.info("created link: %s", response)
|
||||
|
||||
# change session state
|
||||
response = core.set_session_state(session_id, core_pb2.SessionState.INSTANTIATION)
|
||||
response = core.set_session_state(
|
||||
session_id, core_pb2.SessionState.INSTANTIATION
|
||||
)
|
||||
logging.info("set session state: %s", response)
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ import logging
|
|||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
|
|
@ -2,8 +2,7 @@ import logging
|
|||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
|
|
@ -2,8 +2,7 @@ import logging
|
|||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
||||
|
||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
Example custom emane model.
|
||||
"""
|
||||
|
||||
from core.emane import emanemanifest
|
||||
from core.emane import emanemodel
|
||||
from core.emane import emanemanifest, emanemodel
|
||||
|
||||
|
||||
class ExampleModel(emanemodel.EmaneModel):
|
||||
|
@ -40,15 +39,11 @@ class ExampleModel(emanemodel.EmaneModel):
|
|||
mac_library = "rfpipemaclayer"
|
||||
mac_xml = "/usr/share/emane/manifest/rfpipemaclayer.xml"
|
||||
mac_defaults = {
|
||||
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml",
|
||||
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||
}
|
||||
mac_config = emanemanifest.parse(mac_xml, mac_defaults)
|
||||
phy_library = None
|
||||
phy_xml = "/usr/share/emane/manifest/emanephy.xml"
|
||||
phy_defaults = {
|
||||
"subid": "1",
|
||||
"propagationmodel": "2ray",
|
||||
"noisemode": "none"
|
||||
}
|
||||
phy_defaults = {"subid": "1", "propagationmodel": "2ray", "noisemode": "none"}
|
||||
phy_config = emanemanifest.parse(phy_xml, phy_defaults)
|
||||
config_ignore = set()
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
Simple example for a user-defined service.
|
||||
"""
|
||||
|
||||
from core.services.coreservices import CoreService
|
||||
from core.services.coreservices import ServiceMode
|
||||
from core.services.coreservices import CoreService, ServiceMode
|
||||
|
||||
|
||||
class MyService(CoreService):
|
||||
|
@ -31,6 +30,7 @@ class MyService(CoreService):
|
|||
only used in NON_BLOCKING mode
|
||||
:var tuple shutdown: shutdown commands to stop this service
|
||||
"""
|
||||
|
||||
name = "MyService"
|
||||
group = "Utility"
|
||||
executables = ()
|
||||
|
|
|
@ -19,14 +19,16 @@ import core.nodes.base
|
|||
import core.nodes.network
|
||||
from core.api.tlv import coreapi, dataconversion
|
||||
from core.api.tlv.coreapi import CoreExecuteTlv
|
||||
from core.emulator.enumerations import CORE_API_PORT
|
||||
from core.emulator.enumerations import EventTlvs
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import LinkTlvs
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import (
|
||||
CORE_API_PORT,
|
||||
EventTlvs,
|
||||
EventTypes,
|
||||
ExecuteTlvs,
|
||||
LinkTlvs,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
)
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import ipaddress
|
||||
|
||||
|
@ -49,7 +51,9 @@ def cmd(node, exec_cmd):
|
|||
tlvdata = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.id)
|
||||
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, exec_num)
|
||||
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, exec_cmd)
|
||||
msg = coreapi.CoreExecMessage.pack(MessageFlags.STRING.value | MessageFlags.TEXT.value, tlvdata)
|
||||
msg = coreapi.CoreExecMessage.pack(
|
||||
MessageFlags.STRING.value | MessageFlags.TEXT.value, tlvdata
|
||||
)
|
||||
node.session.broker.handlerawmsg(msg)
|
||||
exec_num += 1
|
||||
|
||||
|
@ -79,10 +83,16 @@ def main():
|
|||
parser = optparse.OptionParser(usage=usagestr)
|
||||
parser.set_defaults(numnodes=5, daemon="127.0.0.1:" + str(CORE_API_PORT))
|
||||
|
||||
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
||||
help="number of nodes")
|
||||
parser.add_option("-d", "--daemon-server", dest="daemon", type=str,
|
||||
help="daemon server IP address")
|
||||
parser.add_option(
|
||||
"-n", "--numnodes", dest="numnodes", type=int, help="number of nodes"
|
||||
)
|
||||
parser.add_option(
|
||||
"-d",
|
||||
"--daemon-server",
|
||||
dest="daemon",
|
||||
type=str,
|
||||
help="daemon server IP address",
|
||||
)
|
||||
|
||||
def usage(msg=None, err=0):
|
||||
sys.stdout.write("\n")
|
||||
|
@ -106,7 +116,8 @@ def main():
|
|||
|
||||
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||
session = Session(1)
|
||||
if "server" in globals():
|
||||
server = globals().get("server")
|
||||
if server:
|
||||
server.addsession(session)
|
||||
|
||||
# distributed setup - connect to daemon server
|
||||
|
@ -132,7 +143,9 @@ def main():
|
|||
|
||||
# Change to configuration state on both machines
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
|
||||
tlvdata = coreapi.CoreEventTlv.pack(
|
||||
EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value
|
||||
)
|
||||
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
||||
|
||||
flags = MessageFlags.ADD.value
|
||||
|
@ -145,11 +158,15 @@ def main():
|
|||
|
||||
number_of_nodes = options.numnodes
|
||||
|
||||
print("creating %d remote nodes with addresses from %s" % (options.numnodes, prefix))
|
||||
print(
|
||||
"creating %d remote nodes with addresses from %s" % (options.numnodes, prefix)
|
||||
)
|
||||
|
||||
# create remote nodes via API
|
||||
for i in range(1, number_of_nodes + 1):
|
||||
node = core.nodes.base.CoreNode(session=session, _id=i, name="n%d" % i, start=False)
|
||||
node = core.nodes.base.CoreNode(
|
||||
session=session, _id=i, name="n%d" % i, start=False
|
||||
)
|
||||
node.setposition(x=150 * i, y=150)
|
||||
node.server = daemon
|
||||
node_data = node.data(flags)
|
||||
|
@ -163,14 +180,20 @@ def main():
|
|||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i))
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
||||
LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i)
|
||||
)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
||||
LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen
|
||||
)
|
||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
||||
session.broker.handlerawmsg(msg)
|
||||
|
||||
# We change the daemon to Instantiation state
|
||||
# We do not change the local session as it would try and build a tunnel and fail
|
||||
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value)
|
||||
tlvdata = coreapi.CoreEventTlv.pack(
|
||||
EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value
|
||||
)
|
||||
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
||||
session.broker.handlerawmsg(msg)
|
||||
|
||||
|
@ -179,8 +202,10 @@ def main():
|
|||
pingip = cmd(n[-1], "ip -4 -o addr show dev eth0").split()[3].split("/")[0]
|
||||
print(cmd(n[1], "ping -c 5 " + pingip))
|
||||
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
||||
print("To stop this session, use the core-cleanup script on the remote daemon server.")
|
||||
raw_input("press enter to exit")
|
||||
print(
|
||||
"To stop this session, use the core-cleanup script on the remote daemon server."
|
||||
)
|
||||
input("press enter to exit")
|
||||
|
||||
|
||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
||||
|
|
|
@ -18,7 +18,14 @@ import core.nodes.base
|
|||
import core.nodes.network
|
||||
from core import constants
|
||||
from core.api.tlv import coreapi, dataconversion
|
||||
from core.emulator.enumerations import CORE_API_PORT, EventTypes, EventTlvs, LinkTlvs, LinkTypes, MessageFlags
|
||||
from core.emulator.enumerations import (
|
||||
CORE_API_PORT,
|
||||
EventTlvs,
|
||||
EventTypes,
|
||||
LinkTlvs,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
)
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import ipaddress
|
||||
|
||||
|
@ -31,10 +38,12 @@ def main():
|
|||
parser = optparse.OptionParser(usage=usagestr)
|
||||
parser.set_defaults(numnodes=5, slave=None)
|
||||
|
||||
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
||||
help="number of nodes")
|
||||
parser.add_option("-s", "--slave-server", dest="slave", type=str,
|
||||
help="slave server IP address")
|
||||
parser.add_option(
|
||||
"-n", "--numnodes", dest="numnodes", type=int, help="number of nodes"
|
||||
)
|
||||
parser.add_option(
|
||||
"-s", "--slave-server", dest="slave", type=str, help="slave server IP address"
|
||||
)
|
||||
|
||||
def usage(msg=None, err=0):
|
||||
sys.stdout.write("\n")
|
||||
|
@ -58,11 +67,12 @@ def main():
|
|||
|
||||
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||
session = Session(1)
|
||||
if 'server' in globals():
|
||||
server = globals().get("server")
|
||||
if server is not None:
|
||||
server.addsession(session)
|
||||
|
||||
# distributed setup - connect to slave server
|
||||
slaveport = options.slave.split(':')
|
||||
slaveport = options.slave.split(":")
|
||||
slave = slaveport[0]
|
||||
if len(slaveport) > 1:
|
||||
port = int(slaveport[1])
|
||||
|
@ -72,15 +82,19 @@ def main():
|
|||
session.broker.addserver(slave, slave, port)
|
||||
session.broker.setupserver(slave)
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
|
||||
tlvdata = coreapi.CoreEventTlv.pack(
|
||||
EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value
|
||||
)
|
||||
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
||||
|
||||
switch = session.create_node(cls=core.nodes.network.SwitchNode, name="switch")
|
||||
switch.setposition(x=80, y=50)
|
||||
num_local = options.numnodes / 2
|
||||
num_remote = options.numnodes / 2 + options.numnodes % 2
|
||||
print("creating %d (%d local / %d remote) nodes with addresses from %s" % \
|
||||
(options.numnodes, num_local, num_remote, prefix))
|
||||
print(
|
||||
"creating %d (%d local / %d remote) nodes with addresses from %s"
|
||||
% (options.numnodes, num_local, num_remote, prefix)
|
||||
)
|
||||
for i in range(1, num_local + 1):
|
||||
node = session.create_node(cls=core.nodes.base.CoreNode, name="n%d" % i, _id=i)
|
||||
node.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||
|
@ -93,7 +107,9 @@ def main():
|
|||
|
||||
# create remote nodes via API
|
||||
for i in range(num_local + 1, options.numnodes + 1):
|
||||
node = core.nodes.base.CoreNode(session=session, _id=i, name="n%d" % i, start=False)
|
||||
node = core.nodes.base.CoreNode(
|
||||
session=session, _id=i, name="n%d" % i, start=False
|
||||
)
|
||||
node.setposition(x=150 * i, y=150)
|
||||
node.server = slave
|
||||
n.append(node)
|
||||
|
@ -107,13 +123,19 @@ def main():
|
|||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i))
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
||||
LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i)
|
||||
)
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
||||
LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen
|
||||
)
|
||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
||||
session.broker.handlerawmsg(msg)
|
||||
|
||||
session.instantiate()
|
||||
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value)
|
||||
tlvdata = coreapi.CoreEventTlv.pack(
|
||||
EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value
|
||||
)
|
||||
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
||||
session.broker.handlerawmsg(msg)
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import time
|
|||
import core.nodes.base
|
||||
import core.nodes.network
|
||||
from core import constants
|
||||
from core.nodes import ipaddress
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import ipaddress
|
||||
|
||||
GBD = 1024.0 * 1024.0
|
||||
|
||||
|
@ -78,27 +78,57 @@ switchlist = []
|
|||
def main():
|
||||
usagestr = "usage: %prog [-h] [options] [args]"
|
||||
parser = optparse.OptionParser(usage=usagestr)
|
||||
parser.set_defaults(waittime=0.2, numnodes=0, bridges=0, retries=0,
|
||||
logfile=None, services=None)
|
||||
parser.set_defaults(
|
||||
waittime=0.2, numnodes=0, bridges=0, retries=0, logfile=None, services=None
|
||||
)
|
||||
|
||||
parser.add_option("-w", "--waittime", dest="waittime", type=float,
|
||||
help="number of seconds to wait between node creation" \
|
||||
" (default = %s)" % parser.defaults["waittime"])
|
||||
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
||||
help="number of nodes (default = unlimited)")
|
||||
parser.add_option("-b", "--bridges", dest="bridges", type=int,
|
||||
help="number of nodes per bridge; 0 = one bridge " \
|
||||
"(def. = %s)" % parser.defaults["bridges"])
|
||||
parser.add_option("-r", "--retry", dest="retries", type=int,
|
||||
help="number of retries on error (default = %s)" % \
|
||||
parser.defaults["retries"])
|
||||
parser.add_option("-l", "--log", dest="logfile", type=str,
|
||||
help="log memory usage to this file (default = %s)" % \
|
||||
parser.defaults["logfile"])
|
||||
parser.add_option("-s", "--services", dest="services", type=str,
|
||||
help="pipe-delimited list of services added to each "
|
||||
"node (default = %s)\n(Example: zebra|OSPFv2|OSPFv3|"
|
||||
"IPForward)" % parser.defaults["services"])
|
||||
parser.add_option(
|
||||
"-w",
|
||||
"--waittime",
|
||||
dest="waittime",
|
||||
type=float,
|
||||
help="number of seconds to wait between node creation"
|
||||
" (default = %s)" % parser.defaults["waittime"],
|
||||
)
|
||||
parser.add_option(
|
||||
"-n",
|
||||
"--numnodes",
|
||||
dest="numnodes",
|
||||
type=int,
|
||||
help="number of nodes (default = unlimited)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-b",
|
||||
"--bridges",
|
||||
dest="bridges",
|
||||
type=int,
|
||||
help="number of nodes per bridge; 0 = one bridge "
|
||||
"(def. = %s)" % parser.defaults["bridges"],
|
||||
)
|
||||
parser.add_option(
|
||||
"-r",
|
||||
"--retry",
|
||||
dest="retries",
|
||||
type=int,
|
||||
help="number of retries on error (default = %s)" % parser.defaults["retries"],
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--log",
|
||||
dest="logfile",
|
||||
type=str,
|
||||
help="log memory usage to this file (default = %s)"
|
||||
% parser.defaults["logfile"],
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--services",
|
||||
dest="services",
|
||||
type=str,
|
||||
help="pipe-delimited list of services added to each "
|
||||
"node (default = %s)\n(Example: zebra|OSPFv2|OSPFv3|"
|
||||
"IPForward)" % parser.defaults["services"],
|
||||
)
|
||||
|
||||
def usage(msg=None, err=0):
|
||||
sys.stdout.write("\n")
|
||||
|
@ -118,7 +148,10 @@ def main():
|
|||
print("Testing how many network namespace nodes this machine can create.")
|
||||
print(" - %s" % linuxversion())
|
||||
mem = memfree()
|
||||
print(" - %.02f GB total memory (%.02f GB swap)" % (mem["total"] / GBD, mem["stotal"] / GBD))
|
||||
print(
|
||||
" - %.02f GB total memory (%.02f GB swap)"
|
||||
% (mem["total"] / GBD, mem["stotal"] / GBD)
|
||||
)
|
||||
print(" - using IPv4 network prefix %s" % prefix)
|
||||
print(" - using wait time of %s" % options.waittime)
|
||||
print(" - using %d nodes per bridge" % options.bridges)
|
||||
|
@ -149,9 +182,15 @@ def main():
|
|||
if 0 < options.bridges <= switch.numnetif():
|
||||
switch = session.create_node(cls=core.nodes.network.SwitchNode)
|
||||
switchlist.append(switch)
|
||||
print("\nAdded bridge %s (%d) for node %d." % (switch.brname, len(switchlist), i))
|
||||
print(
|
||||
"\nAdded bridge %s (%d) for node %d."
|
||||
% (switch.brname, len(switchlist), i)
|
||||
)
|
||||
except Exception as e:
|
||||
print("At %d bridges (%d nodes) caught exception:\n%s\n" % (len(switchlist), i - 1, e))
|
||||
print(
|
||||
"At %d bridges (%d nodes) caught exception:\n%s\n"
|
||||
% (len(switchlist), i - 1, e)
|
||||
)
|
||||
break
|
||||
|
||||
# create a node
|
||||
|
@ -164,11 +203,11 @@ def main():
|
|||
session.services.boot_services(n)
|
||||
nodelist.append(n)
|
||||
if i % 25 == 0:
|
||||
print("\n%s nodes created " % i,)
|
||||
print("\n%s nodes created " % i)
|
||||
mem = memfree()
|
||||
free = mem["free"] + mem["buff"] + mem["cached"]
|
||||
swap = mem["stotal"] - mem["sfree"]
|
||||
print("(%.02f/%.02f GB free/swap)" % (free / GBD, swap / GBD),)
|
||||
print("(%.02f/%.02f GB free/swap)" % (free / GBD, swap / GBD))
|
||||
if lfp:
|
||||
lfp.write("%d," % i)
|
||||
lfp.write("%s\n" % ",".join(str(mem[x]) for x in MEMKEYS))
|
||||
|
|
|
@ -19,7 +19,6 @@ from string import Template
|
|||
import core.nodes.base
|
||||
import core.nodes.network
|
||||
from core.constants import QUAGGA_STATE_DIR
|
||||
# this is the /etc/core/core.conf default
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import ipaddress
|
||||
from core.utils import check_cmd
|
||||
|
@ -42,7 +41,9 @@ except OSError:
|
|||
class ManetNode(core.nodes.base.CoreNode):
|
||||
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
|
||||
"""
|
||||
conftemp = Template("""\
|
||||
|
||||
conftemp = Template(
|
||||
"""\
|
||||
interface eth0
|
||||
ip address $ipaddr
|
||||
ipv6 ospf6 instance-id 65
|
||||
|
@ -59,12 +60,12 @@ router ospf6
|
|||
interface eth0 area 0.0.0.0
|
||||
!
|
||||
ip forwarding
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
confdir = "/usr/local/etc/quagga"
|
||||
|
||||
def __init__(self, core, ipaddr, routerid=None,
|
||||
_id=None, name=None, nodedir=None):
|
||||
def __init__(self, core, ipaddr, routerid=None, _id=None, name=None, nodedir=None):
|
||||
if routerid is None:
|
||||
routerid = ipaddr.split("/")[0]
|
||||
self.ipaddr = ipaddr
|
||||
|
@ -74,8 +75,7 @@ ip forwarding
|
|||
self.privatedir(QUAGGA_STATE_DIR)
|
||||
|
||||
def qconf(self):
|
||||
return self.conftemp.substitute(ipaddr=self.ipaddr,
|
||||
routerid=self.routerid)
|
||||
return self.conftemp.substitute(ipaddr=self.ipaddr, routerid=self.routerid)
|
||||
|
||||
def config(self):
|
||||
filename = os.path.join(self.confdir, "Quagga.conf")
|
||||
|
@ -120,7 +120,11 @@ waitfile $STATEDIR/zebra.vty
|
|||
waitfile $STATEDIR/ospf6d.vty
|
||||
|
||||
vtysh -b
|
||||
""" % (QUAGGA_STATE_DIR, quagga_path, quagga_path)
|
||||
""" % (
|
||||
QUAGGA_STATE_DIR,
|
||||
quagga_path,
|
||||
quagga_path,
|
||||
)
|
||||
|
||||
|
||||
class Route(object):
|
||||
|
@ -130,15 +134,20 @@ class Route(object):
|
|||
try:
|
||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid prefix given to Route object: %s\n%s" % (prefix, e))
|
||||
raise ValueError(
|
||||
"Invalid prefix given to Route object: %s\n%s" % (prefix, e)
|
||||
)
|
||||
self.gw = gw
|
||||
self.metric = metric
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
return self.prefix == other.prefix and self.gw == other.gw and \
|
||||
self.metric == other.metric
|
||||
except:
|
||||
return (
|
||||
self.prefix == other.prefix
|
||||
and self.gw == other.gw
|
||||
and self.metric == other.metric
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
|
@ -169,13 +178,13 @@ class ManetExperiment(object):
|
|||
self.logbegin()
|
||||
|
||||
def info(self, msg):
|
||||
''' Utility method for writing output to stdout. '''
|
||||
""" Utility method for writing output to stdout. """
|
||||
print(msg)
|
||||
sys.stdout.flush()
|
||||
self.log(msg)
|
||||
|
||||
def warn(self, msg):
|
||||
''' Utility method for writing output to stderr. '''
|
||||
""" Utility method for writing output to stderr. """
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.flush()
|
||||
self.log(msg)
|
||||
|
@ -193,8 +202,7 @@ class ManetExperiment(object):
|
|||
if not self.logfp:
|
||||
return
|
||||
end = datetime.datetime.now()
|
||||
self.log("ospfmanetmdrtest end: %s (%s)\n" % \
|
||||
(end.ctime(), end - self.start))
|
||||
self.log("ospfmanetmdrtest end: %s (%s)\n" % (end.ctime(), end - self.start))
|
||||
self.logfp.flush()
|
||||
self.logfp.close()
|
||||
self.logfp = None
|
||||
|
@ -245,7 +253,9 @@ class ManetExperiment(object):
|
|||
self.net = self.session.create_node(cls=core.nodes.network.WlanNode)
|
||||
for i in range(1, numnodes + 1):
|
||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
||||
tmp = self.session.create_node(cls=ManetNode, ipaddr=addr, _id="%d" % i, name="n%d" % i)
|
||||
tmp = self.session.create_node(
|
||||
cls=ManetNode, ipaddr=addr, _id="%d" % i, name="n%d" % i
|
||||
)
|
||||
tmp.newnetif(self.net, [addr])
|
||||
self.nodes.append(tmp)
|
||||
# connect nodes with probability linkprob
|
||||
|
@ -301,8 +311,9 @@ class ManetExperiment(object):
|
|||
break
|
||||
if not connected:
|
||||
msg = "All routers do not form a CDS"
|
||||
self.warn("XXX %s: not in CDS; neighbors: %s" % \
|
||||
(n.routerid, nbrs[n.routerid]))
|
||||
self.warn(
|
||||
"XXX %s: not in CDS; neighbors: %s" % (n.routerid, nbrs[n.routerid])
|
||||
)
|
||||
if self.verbose:
|
||||
self.info(msg)
|
||||
|
||||
|
@ -315,8 +326,9 @@ class ManetExperiment(object):
|
|||
db = lsdbs[n.routerid]
|
||||
if lsdbs[prev.routerid] != db:
|
||||
msg = "LSDBs of all routers are not consistent"
|
||||
self.warn("XXX LSDBs inconsistent for %s and %s" % \
|
||||
(n.routerid, prev.routerid))
|
||||
self.warn(
|
||||
"XXX LSDBs inconsistent for %s and %s" % (n.routerid, prev.routerid)
|
||||
)
|
||||
i = 0
|
||||
for entry in lsdbs[n.routerid].split("\n"):
|
||||
preventries = lsdbs[prev.routerid].split("\n")
|
||||
|
@ -355,6 +367,7 @@ class ManetExperiment(object):
|
|||
|
||||
class Cmd:
|
||||
""" Helper class for running a command on a node and parsing the result. """
|
||||
|
||||
args = ""
|
||||
|
||||
def __init__(self, node, verbose=False):
|
||||
|
@ -366,12 +379,12 @@ class Cmd:
|
|||
self.verbose = verbose
|
||||
|
||||
def info(self, msg):
|
||||
''' Utility method for writing output to stdout.'''
|
||||
""" Utility method for writing output to stdout."""
|
||||
print(msg)
|
||||
sys.stdout.flush()
|
||||
|
||||
def warn(self, msg):
|
||||
''' Utility method for writing output to stderr. '''
|
||||
""" Utility method for writing output to stderr. """
|
||||
sys.stderr.write("XXX %s:" % self.node.routerid, msg)
|
||||
sys.stderr.flush()
|
||||
|
||||
|
@ -412,6 +425,7 @@ class VtyshCmd(Cmd):
|
|||
|
||||
class Ospf6NeighState(VtyshCmd):
|
||||
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
|
||||
|
||||
args = "show ipv6 ospf6 neighbor"
|
||||
|
||||
def parse(self):
|
||||
|
@ -435,6 +449,7 @@ class Ospf6NeighState(VtyshCmd):
|
|||
|
||||
class Ospf6MdrLevel(VtyshCmd):
|
||||
""" Retrieve the OSPFv3 MDR level for a node. """
|
||||
|
||||
args = "show ipv6 ospf6 mdrlevel"
|
||||
|
||||
def parse(self):
|
||||
|
@ -442,7 +457,7 @@ class Ospf6MdrLevel(VtyshCmd):
|
|||
# TODO: handle multiple interfaces
|
||||
field = line.split()
|
||||
mdrlevel = field[4]
|
||||
if not mdrlevel in ("MDR", "BMDR", "OTHER"):
|
||||
if mdrlevel not in ("MDR", "BMDR", "OTHER"):
|
||||
self.warn("mdrlevel: %s" % mdrlevel)
|
||||
if self.verbose:
|
||||
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
|
||||
|
@ -451,6 +466,7 @@ class Ospf6MdrLevel(VtyshCmd):
|
|||
|
||||
class Ospf6Database(VtyshCmd):
|
||||
""" Retrieve the OSPFv3 LSDB summary for a node. """
|
||||
|
||||
args = "show ipv6 ospf6 database"
|
||||
|
||||
def parse(self):
|
||||
|
@ -469,6 +485,7 @@ class ZebraRoutes(VtyshCmd):
|
|||
""" Return a list of Route objects for a node based on its zebra
|
||||
routing table.
|
||||
"""
|
||||
|
||||
args = "show ip route"
|
||||
|
||||
def parse(self):
|
||||
|
@ -510,6 +527,7 @@ class KernelRoutes(Cmd):
|
|||
""" Return a list of Route objects for a node based on its kernel
|
||||
routing table.
|
||||
"""
|
||||
|
||||
args = ("/sbin/ip", "route", "show")
|
||||
|
||||
def parse(self):
|
||||
|
@ -547,18 +565,32 @@ def main():
|
|||
parser = optparse.OptionParser(usage=usagestr)
|
||||
parser.set_defaults(numnodes=10, linkprob=0.35, delay=20, seed=None)
|
||||
|
||||
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
||||
help="number of nodes")
|
||||
parser.add_option("-p", "--linkprob", dest="linkprob", type=float,
|
||||
help="link probabilty")
|
||||
parser.add_option("-d", "--delay", dest="delay", type=float,
|
||||
help="wait time before checking")
|
||||
parser.add_option("-s", "--seed", dest="seed", type=int,
|
||||
help="specify integer to use for random seed")
|
||||
parser.add_option("-v", "--verbose", dest="verbose",
|
||||
action="store_true", help="be more verbose")
|
||||
parser.add_option("-l", "--logfile", dest="logfile", type=str,
|
||||
help="log detailed output to the specified file")
|
||||
parser.add_option(
|
||||
"-n", "--numnodes", dest="numnodes", type=int, help="number of nodes"
|
||||
)
|
||||
parser.add_option(
|
||||
"-p", "--linkprob", dest="linkprob", type=float, help="link probabilty"
|
||||
)
|
||||
parser.add_option(
|
||||
"-d", "--delay", dest="delay", type=float, help="wait time before checking"
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--seed",
|
||||
dest="seed",
|
||||
type=int,
|
||||
help="specify integer to use for random seed",
|
||||
)
|
||||
parser.add_option(
|
||||
"-v", "--verbose", dest="verbose", action="store_true", help="be more verbose"
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--logfile",
|
||||
dest="logfile",
|
||||
type=str,
|
||||
help="log detailed output to the specified file",
|
||||
)
|
||||
|
||||
def usage(msg=None, err=0):
|
||||
sys.stdout.write("\n")
|
||||
|
@ -584,8 +616,10 @@ def main():
|
|||
random.seed(options.seed)
|
||||
|
||||
me = ManetExperiment(options=options, start=datetime.datetime.now())
|
||||
me.info("creating topology: numnodes = %s; linkprob = %s" % \
|
||||
(options.numnodes, options.linkprob))
|
||||
me.info(
|
||||
"creating topology: numnodes = %s; linkprob = %s"
|
||||
% (options.numnodes, options.linkprob)
|
||||
)
|
||||
me.topology(options.numnodes, options.linkprob)
|
||||
|
||||
me.info("waiting %s sec" % options.delay)
|
||||
|
|
|
@ -1,847 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# Copyright (c)2011-2014 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
"""
|
||||
wlanemanetests.py - This script tests the performance of the WLAN device in
|
||||
CORE by measuring various metrics:
|
||||
- delay experienced when pinging end-to-end
|
||||
- maximum TCP throughput achieved using iperf end-to-end
|
||||
- the CPU used and loss experienced when running an MGEN flow of UDP traffic
|
||||
|
||||
All MANET nodes are arranged in a row, so that any given node can only
|
||||
communicate with the node to its right or to its left. Performance is measured
|
||||
using traffic that travels across each hop in the network. Static /32 routing
|
||||
is used instead of any dynamic routing protocol.
|
||||
|
||||
Various underlying network types are tested:
|
||||
- bridged (the CORE default, uses ebtables)
|
||||
- bridged with netem (add link effects to the bridge using tc queues)
|
||||
- EMANE bypass - the bypass model just forwards traffic
|
||||
- EMANE RF-PIPE - the bandwidth (bitrate) is set very high / no restrictions
|
||||
- EMANE RF-PIPE - bandwidth is set similar to netem case
|
||||
- EMANE RF-PIPE - default connectivity is off and pathloss events are
|
||||
generated to connect the nodes in a line
|
||||
|
||||
Results are printed/logged in CSV format.
|
||||
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import math
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import core.nodes.base
|
||||
import core.nodes.network
|
||||
from core import emane
|
||||
from core.emane.bypass import EmaneBypassModel
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import ipaddress
|
||||
|
||||
try:
|
||||
import emaneeventservice
|
||||
import emaneeventpathloss
|
||||
except Exception as e:
|
||||
try:
|
||||
from emanesh.events import EventService
|
||||
from emanesh.events import PathlossEvent
|
||||
except Exception as e2:
|
||||
raise ImportError("failed to import EMANE Python bindings:\n%s\n%s" % (e, e2))
|
||||
|
||||
# global Experiment object (for interaction with "python -i")
|
||||
exp = None
|
||||
|
||||
|
||||
# move these to core.misc.utils
|
||||
def readstat():
|
||||
f = open("/proc/stat", "r")
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
return lines
|
||||
|
||||
|
||||
def numcpus():
|
||||
lines = readstat()
|
||||
n = 0
|
||||
for l in lines[1:]:
|
||||
if l[:3] != "cpu":
|
||||
break
|
||||
n += 1
|
||||
return n
|
||||
|
||||
|
||||
def getcputimes(line):
|
||||
# return (user, nice, sys, idle) from a /proc/stat cpu line
|
||||
# assume columns are:
|
||||
# cpu# user nice sys idle iowait irq softirq steal guest (man 5 proc)
|
||||
items = line.split()
|
||||
(user, nice, sys, idle) = map(lambda (x): int(x), items[1:5])
|
||||
return [user, nice, sys, idle]
|
||||
|
||||
|
||||
def calculatecpu(timesa, timesb):
|
||||
for i in range(len(timesa)):
|
||||
timesb[i] -= timesa[i]
|
||||
total = sum(timesb)
|
||||
if total == 0:
|
||||
return 0.0
|
||||
else:
|
||||
# subtract % time spent in idle time
|
||||
return 100 - ((100.0 * timesb[-1]) / total)
|
||||
|
||||
|
||||
# end move these to core.misc.utils
|
||||
|
||||
class Cmd(object):
|
||||
""" Helper class for running a command on a node and parsing the result. """
|
||||
args = ""
|
||||
|
||||
def __init__(self, node, verbose=False):
|
||||
""" Initialize with a CoreNode (LxcNode) """
|
||||
self.id = None
|
||||
self.stdin = None
|
||||
self.out = None
|
||||
self.node = node
|
||||
self.verbose = verbose
|
||||
|
||||
def info(self, msg):
|
||||
""" Utility method for writing output to stdout."""
|
||||
print(msg)
|
||||
sys.stdout.flush()
|
||||
|
||||
def warn(self, msg):
|
||||
""" Utility method for writing output to stderr. """
|
||||
sys.stderr.write("XXX %s:" % self.node.name, msg)
|
||||
sys.stderr.flush()
|
||||
|
||||
def run(self):
|
||||
""" This is the primary method used for running this command. """
|
||||
self.open()
|
||||
status = self.id.wait()
|
||||
r = self.parse()
|
||||
self.cleanup()
|
||||
return r
|
||||
|
||||
def open(self):
|
||||
""" Exceute call to node.popen(). """
|
||||
self.id, self.stdin, self.out, self.err = self.node.client.popen(self.args)
|
||||
|
||||
def parse(self):
|
||||
""" This method is overloaded by child classes and should return some
|
||||
result.
|
||||
"""
|
||||
return None
|
||||
|
||||
def cleanup(self):
|
||||
""" Close the Popen channels."""
|
||||
self.stdin.close()
|
||||
self.out.close()
|
||||
self.err.close()
|
||||
tmp = self.id.wait()
|
||||
if tmp:
|
||||
self.warn("nonzero exit status:", tmp)
|
||||
|
||||
|
||||
class ClientServerCmd(Cmd):
|
||||
""" Helper class for running a command on a node and parsing the result. """
|
||||
args = ""
|
||||
client_args = ""
|
||||
|
||||
def __init__(self, node, client_node, verbose=False):
|
||||
""" Initialize with two CoreNodes, node is the server """
|
||||
Cmd.__init__(self, node, verbose)
|
||||
self.client_node = client_node
|
||||
|
||||
def run(self):
|
||||
""" Run the server command, then the client command, then
|
||||
kill the server """
|
||||
self.open() # server
|
||||
self.client_open() # client
|
||||
status = self.client_id.wait()
|
||||
# stop the server
|
||||
self.node.cmd_output(["killall", self.args[0]])
|
||||
r = self.parse()
|
||||
self.cleanup()
|
||||
return r
|
||||
|
||||
def client_open(self):
|
||||
""" Exceute call to client_node.popen(). """
|
||||
self.client_id, self.client_stdin, self.client_out, self.client_err = \
|
||||
self.client_node.client.popen(self.client_args)
|
||||
|
||||
def parse(self):
|
||||
""" This method is overloaded by child classes and should return some
|
||||
result.
|
||||
"""
|
||||
return None
|
||||
|
||||
def cleanup(self):
|
||||
""" Close the Popen channels."""
|
||||
self.stdin.close()
|
||||
self.out.close()
|
||||
self.err.close()
|
||||
tmp = self.id.wait()
|
||||
if tmp:
|
||||
self.warn("nonzero exit status: %s" % tmp)
|
||||
self.warn("command was: %s" % (self.args,))
|
||||
|
||||
|
||||
class PingCmd(Cmd):
|
||||
""" Test latency using ping.
|
||||
"""
|
||||
|
||||
def __init__(self, node, verbose=False, addr=None, count=50, interval=0.1, ):
|
||||
Cmd.__init__(self, node, verbose)
|
||||
self.addr = addr
|
||||
self.count = count
|
||||
self.interval = interval
|
||||
self.args = ["ping", "-q", "-c", "%s" % count, "-i", "%s" % interval, addr]
|
||||
|
||||
def run(self):
|
||||
if self.verbose:
|
||||
self.info("%s initial test ping (max 1 second)..." % self.node.name)
|
||||
(status, result) = self.node.cmd_output(["ping", "-q", "-c", "1", "-w", "1", self.addr])
|
||||
if status != 0:
|
||||
self.warn("initial ping from %s to %s failed! result:\n%s" %
|
||||
(self.node.name, self.addr, result))
|
||||
return 0.0, 0.0
|
||||
if self.verbose:
|
||||
self.info("%s pinging %s (%d seconds)..." %
|
||||
(self.node.name, self.addr, self.count * self.interval))
|
||||
return Cmd.run(self)
|
||||
|
||||
def parse(self):
|
||||
lines = self.out.readlines()
|
||||
avg_latency = 0
|
||||
mdev = 0
|
||||
try:
|
||||
stats_str = lines[-1].split("=")[1]
|
||||
stats = stats_str.split("/")
|
||||
avg_latency = float(stats[1])
|
||||
mdev = float(stats[3].split(" ")[0])
|
||||
except:
|
||||
self.warn("ping parsing exception: %s" % e)
|
||||
return avg_latency, mdev
|
||||
|
||||
|
||||
class IperfCmd(ClientServerCmd):
|
||||
""" Test throughput using iperf.
|
||||
"""
|
||||
|
||||
def __init__(self, node, client_node, verbose=False, addr=None, time=10):
|
||||
# node is the server
|
||||
ClientServerCmd.__init__(self, node, client_node, verbose)
|
||||
self.addr = addr
|
||||
self.time = time
|
||||
# -s server, -y c CSV report output
|
||||
self.args = ["iperf", "-s", "-y", "c"]
|
||||
self.client_args = ["iperf", "-c", self.addr, "-t", "%s" % self.time]
|
||||
|
||||
def run(self):
|
||||
if self.verbose:
|
||||
self.info("Launching the iperf server on %s..." % self.node.name)
|
||||
self.info("Running the iperf client on %s (%s seconds)..." % \
|
||||
(self.client_node.name, self.time))
|
||||
return ClientServerCmd.run(self)
|
||||
|
||||
def parse(self):
|
||||
lines = self.out.readlines()
|
||||
try:
|
||||
bps = int(lines[-1].split(",")[-1].strip("\n"))
|
||||
except Exception as e:
|
||||
self.warn("iperf parsing exception: %s" % e)
|
||||
bps = 0
|
||||
return bps
|
||||
|
||||
|
||||
class MgenCmd(ClientServerCmd):
|
||||
""" Run a test traffic flow using an MGEN sender and receiver.
|
||||
"""
|
||||
|
||||
def __init__(self, node, client_node, verbose=False, addr=None, time=10,
|
||||
rate=512):
|
||||
ClientServerCmd.__init__(self, node, client_node, verbose)
|
||||
self.addr = addr
|
||||
self.time = time
|
||||
self.args = ["mgen", "event", "listen udp 5000", "output",
|
||||
"/var/log/mgen.log"]
|
||||
self.rate = rate
|
||||
sendevent = "ON 1 UDP DST %s/5000 PERIODIC [%s]" % \
|
||||
(addr, self.mgenrate(self.rate))
|
||||
stopevent = "%s OFF 1" % time
|
||||
self.client_args = ["mgen", "event", sendevent, "event", stopevent,
|
||||
"output", "/var/log/mgen.log"]
|
||||
|
||||
@staticmethod
|
||||
def mgenrate(kbps):
|
||||
""" Return a MGEN periodic rate string for the given kilobits-per-sec.
|
||||
Assume 1500 byte MTU, 20-byte IP + 8-byte UDP headers, leaving
|
||||
1472 bytes for data.
|
||||
"""
|
||||
bps = (kbps / 8) * 1000.0
|
||||
maxdata = 1472
|
||||
pps = math.ceil(bps / maxdata)
|
||||
return "%s %s" % (pps, maxdata)
|
||||
|
||||
def run(self):
|
||||
if self.verbose:
|
||||
self.info("Launching the MGEN receiver on %s..." % self.node.name)
|
||||
self.info("Running the MGEN sender on %s (%s seconds)..." % \
|
||||
(self.client_node.name, self.time))
|
||||
return ClientServerCmd.run(self)
|
||||
|
||||
def cleanup(self):
|
||||
""" Close the Popen channels."""
|
||||
self.stdin.close()
|
||||
self.out.close()
|
||||
self.err.close()
|
||||
# non-zero mgen exit status OK
|
||||
tmp = self.id.wait()
|
||||
|
||||
def parse(self):
|
||||
""" Check MGEN receiver"s log file for packet sequence numbers, and
|
||||
return the percentage of lost packets.
|
||||
"""
|
||||
logfile = os.path.join(self.node.nodedir, "var.log/mgen.log")
|
||||
f = open(logfile, "r")
|
||||
numlost = 0
|
||||
lastseq = 0
|
||||
for line in f.readlines():
|
||||
fields = line.split()
|
||||
if fields[1] != "RECV":
|
||||
continue
|
||||
try:
|
||||
seq = int(fields[4].split(">")[1])
|
||||
except:
|
||||
self.info("Unexpected MGEN line:\n%s" % fields)
|
||||
if seq > (lastseq + 1):
|
||||
numlost += seq - (lastseq + 1)
|
||||
lastseq = seq
|
||||
f.close()
|
||||
if lastseq > 0:
|
||||
loss = 100.0 * numlost / lastseq
|
||||
else:
|
||||
loss = 0
|
||||
if self.verbose:
|
||||
self.info("Receiver log shows %d of %d packets lost" % \
|
||||
(numlost, lastseq))
|
||||
return loss
|
||||
|
||||
|
||||
class Experiment(object):
|
||||
""" Experiment object to organize tests.
|
||||
"""
|
||||
|
||||
def __init__(self, opt, start):
|
||||
""" Initialize with opt and start time. """
|
||||
self.session = None
|
||||
# node list
|
||||
self.nodes = []
|
||||
# WLAN network
|
||||
self.net = None
|
||||
self.verbose = opt.verbose
|
||||
# dict from OptionParser
|
||||
self.opt = opt
|
||||
self.start = start
|
||||
self.numping = opt.numping
|
||||
self.numiperf = opt.numiperf
|
||||
self.nummgen = opt.nummgen
|
||||
self.logbegin()
|
||||
|
||||
def info(self, msg):
|
||||
""" Utility method for writing output to stdout. """
|
||||
print(msg)
|
||||
sys.stdout.flush()
|
||||
self.log(msg)
|
||||
|
||||
def warn(self, msg):
|
||||
""" Utility method for writing output to stderr. """
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.flush()
|
||||
self.log(msg)
|
||||
|
||||
def logbegin(self):
|
||||
""" Start logging. """
|
||||
self.logfp = None
|
||||
if not self.opt.logfile:
|
||||
return
|
||||
self.logfp = open(self.opt.logfile, "w")
|
||||
self.log("%s begin: %s\n" % (sys.argv[0], self.start.ctime()))
|
||||
self.log("%s args: %s\n" % (sys.argv[0], sys.argv[1:]))
|
||||
(sysname, rel, ver, machine, nodename) = os.uname()
|
||||
self.log("%s %s %s %s on %s" % (sysname, rel, ver, machine, nodename))
|
||||
|
||||
def logend(self):
|
||||
""" End logging. """
|
||||
if not self.logfp:
|
||||
return
|
||||
end = datetime.datetime.now()
|
||||
self.log("%s end: %s (%s)\n" % \
|
||||
(sys.argv[0], end.ctime(), end - self.start))
|
||||
self.logfp.flush()
|
||||
self.logfp.close()
|
||||
self.logfp = None
|
||||
|
||||
def log(self, msg):
|
||||
""" Write to the log file, if any. """
|
||||
if not self.logfp:
|
||||
return
|
||||
self.logfp.write(msg)
|
||||
|
||||
def reset(self):
|
||||
""" Prepare for another experiment run.
|
||||
"""
|
||||
if self.session:
|
||||
self.session.shutdown()
|
||||
del self.session
|
||||
self.session = None
|
||||
self.nodes = []
|
||||
self.net = None
|
||||
|
||||
def createbridgedsession(self, numnodes, verbose=False):
|
||||
""" Build a topology consisting of the given number of LxcNodes
|
||||
connected to a WLAN.
|
||||
"""
|
||||
# IP subnet
|
||||
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
||||
self.session = Session(1)
|
||||
# emulated network
|
||||
self.net = self.session.create_node(cls=core.nodes.network.WlanNode, name="wlan1")
|
||||
prev = None
|
||||
for i in range(1, numnodes + 1):
|
||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
||||
tmp = self.session.create_node(cls=core.nodes.base.CoreNode, _id=i, name="n%d" % i)
|
||||
tmp.newnetif(self.net, [addr])
|
||||
self.nodes.append(tmp)
|
||||
self.session.services.add_services(tmp, "router", "IPForward")
|
||||
self.session.services.boot_services(tmp)
|
||||
self.staticroutes(i, prefix, numnodes)
|
||||
|
||||
# link each node in a chain, with the previous node
|
||||
if prev:
|
||||
self.net.link(prev.netif(0), tmp.netif(0))
|
||||
prev = tmp
|
||||
|
||||
def createemanesession(self, numnodes, verbose=False, cls=None, values=None):
|
||||
""" Build a topology consisting of the given number of LxcNodes
|
||||
connected to an EMANE WLAN.
|
||||
"""
|
||||
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
||||
self.session = Session(2)
|
||||
self.session.node_count = str(numnodes + 1)
|
||||
self.session.master = True
|
||||
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
||||
self.session.location.refscale = 150.0
|
||||
self.session.emane.loadmodels()
|
||||
self.net = self.session.create_node(cls=EmaneNode, _id=numnodes + 1, name="wlan1")
|
||||
self.net.verbose = verbose
|
||||
# self.session.emane.addobj(self.net)
|
||||
for i in range(1, numnodes + 1):
|
||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
||||
tmp = self.session.create_node(cls=core.nodes.base.CoreNode, _id=i, name="n%d" % i)
|
||||
# tmp.setposition(i * 20, 50, None)
|
||||
tmp.setposition(50, 50, None)
|
||||
tmp.newnetif(self.net, [addr])
|
||||
self.nodes.append(tmp)
|
||||
self.session.services.add_services(tmp, "router", "IPForward")
|
||||
|
||||
if values is None:
|
||||
values = cls.getdefaultvalues()
|
||||
self.session.emane.setconfig(self.net.id, cls.name, values)
|
||||
self.session.instantiate()
|
||||
|
||||
self.info("waiting %s sec (TAP bring-up)" % 2)
|
||||
time.sleep(2)
|
||||
|
||||
for i in range(1, numnodes + 1):
|
||||
tmp = self.nodes[i - 1]
|
||||
self.session.services.boot_services(tmp)
|
||||
self.staticroutes(i, prefix, numnodes)
|
||||
|
||||
def setnodes(self):
|
||||
""" Set the sender and receiver nodes for use in this experiment,
|
||||
along with the address of the receiver to be used.
|
||||
"""
|
||||
self.firstnode = self.nodes[0]
|
||||
self.lastnode = self.nodes[-1]
|
||||
self.lastaddr = self.lastnode.netif(0).addrlist[0].split("/")[0]
|
||||
|
||||
def staticroutes(self, i, prefix, numnodes):
|
||||
""" Add static routes on node number i to the other nodes in the chain.
|
||||
"""
|
||||
routecmd = ["/sbin/ip", "route", "add"]
|
||||
node = self.nodes[i - 1]
|
||||
neigh_left = ""
|
||||
neigh_right = ""
|
||||
# add direct interface routes first
|
||||
if i > 1:
|
||||
neigh_left = "%s" % prefix.addr(i - 1)
|
||||
cmd = routecmd + [neigh_left, "dev", node.netif(0).name]
|
||||
(status, result) = node.cmd_output(cmd)
|
||||
if status != 0:
|
||||
self.warn("failed to add interface route: %s" % cmd)
|
||||
if i < numnodes:
|
||||
neigh_right = "%s" % prefix.addr(i + 1)
|
||||
cmd = routecmd + [neigh_right, "dev", node.netif(0).name]
|
||||
(status, result) = node.cmd_output(cmd)
|
||||
if status != 0:
|
||||
self.warn("failed to add interface route: %s" % cmd)
|
||||
|
||||
# add static routes to all other nodes via left/right neighbors
|
||||
for j in range(1, numnodes + 1):
|
||||
if abs(j - i) < 2:
|
||||
continue
|
||||
addr = "%s" % prefix.addr(j)
|
||||
if j < i:
|
||||
gw = neigh_left
|
||||
else:
|
||||
gw = neigh_right
|
||||
cmd = routecmd + [addr, "via", gw]
|
||||
(status, result) = node.cmd_output(cmd)
|
||||
if status != 0:
|
||||
self.warn("failed to add route: %s" % cmd)
|
||||
|
||||
def setpathloss(self, numnodes):
|
||||
""" Send EMANE pathloss events to connect all NEMs in a chain.
|
||||
"""
|
||||
if self.session.emane.version < self.session.emane.EMANE091:
|
||||
service = emaneeventservice.EventService()
|
||||
e = emaneeventpathloss.EventPathloss(1)
|
||||
old = True
|
||||
else:
|
||||
if self.session.emane.version == self.session.emane.EMANE091:
|
||||
dev = "lo"
|
||||
else:
|
||||
dev = self.session.obj("ctrlnet").brname
|
||||
service = EventService(eventchannel=("224.1.2.8", 45703, dev),
|
||||
otachannel=None)
|
||||
old = False
|
||||
|
||||
for i in range(1, numnodes + 1):
|
||||
rxnem = i
|
||||
# inform rxnem that it can hear node to the left with 10dB noise
|
||||
txnem = rxnem - 1
|
||||
if txnem > 0:
|
||||
if old:
|
||||
e.set(0, txnem, 10.0, 10.0)
|
||||
service.publish(emaneeventpathloss.EVENT_ID,
|
||||
emaneeventservice.PLATFORMID_ANY, rxnem,
|
||||
emaneeventservice.COMPONENTID_ANY, e.export())
|
||||
else:
|
||||
e = PathlossEvent()
|
||||
e.append(txnem, forward=10.0, reverse=10.0)
|
||||
service.publish(rxnem, e)
|
||||
# inform rxnem that it can hear node to the right with 10dB noise
|
||||
txnem = rxnem + 1
|
||||
if txnem > numnodes:
|
||||
continue
|
||||
if old:
|
||||
e.set(0, txnem, 10.0, 10.0)
|
||||
service.publish(emaneeventpathloss.EVENT_ID,
|
||||
emaneeventservice.PLATFORMID_ANY, rxnem,
|
||||
emaneeventservice.COMPONENTID_ANY, e.export())
|
||||
else:
|
||||
e = PathlossEvent()
|
||||
e.append(txnem, forward=10.0, reverse=10.0)
|
||||
service.publish(rxnem, e)
|
||||
|
||||
def setneteffects(self, bw=None, delay=None):
|
||||
""" Set link effects for all interfaces attached to the network node.
|
||||
"""
|
||||
if not self.net:
|
||||
self.warn("failed to set effects: no network node")
|
||||
return
|
||||
for netif in self.net.netifs():
|
||||
self.net.linkconfig(netif, bw=bw, delay=delay)
|
||||
|
||||
def runalltests(self, title=""):
|
||||
""" Convenience helper to run all defined experiment tests.
|
||||
If tests are run multiple times, this returns the average of
|
||||
those runs.
|
||||
"""
|
||||
duration = self.opt.duration
|
||||
rate = self.opt.rate
|
||||
if len(title) > 0:
|
||||
self.info("----- running %s tests (duration=%s, rate=%s) -----" % \
|
||||
(title, duration, rate))
|
||||
(latency, mdev, throughput, cpu, loss) = (0, 0, 0, 0, 0)
|
||||
|
||||
self.info("number of runs: ping=%d, iperf=%d, mgen=%d" % \
|
||||
(self.numping, self.numiperf, self.nummgen))
|
||||
|
||||
if self.numping > 0:
|
||||
(latency, mdev) = self.pingtest(count=self.numping)
|
||||
|
||||
if self.numiperf > 0:
|
||||
throughputs = []
|
||||
for i in range(1, self.numiperf + 1):
|
||||
throughput = self.iperftest(time=duration)
|
||||
if self.numiperf > 1:
|
||||
throughputs += throughput
|
||||
# iperf is very CPU intensive
|
||||
time.sleep(1)
|
||||
if self.numiperf > 1:
|
||||
throughput = sum(throughputs) / len(throughputs)
|
||||
self.info("throughputs=%s" % ["%.2f" % v for v in throughputs])
|
||||
|
||||
if self.nummgen > 0:
|
||||
cpus = []
|
||||
losses = []
|
||||
for i in range(1, self.nummgen + 1):
|
||||
(cpu, loss) = self.cputest(time=duration, rate=rate)
|
||||
if self.nummgen > 1:
|
||||
cpus += cpu,
|
||||
losses += loss,
|
||||
if self.nummgen > 1:
|
||||
cpu = sum(cpus) / len(cpus)
|
||||
loss = sum(losses) / len(losses)
|
||||
self.info("cpus=%s" % ["%.2f" % v for v in cpus])
|
||||
self.info("losses=%s" % ["%.2f" % v for v in losses])
|
||||
|
||||
return latency, mdev, throughput, cpu, loss
|
||||
|
||||
def pingtest(self, count=50):
|
||||
""" Ping through a chain of nodes and report the average latency.
|
||||
"""
|
||||
p = PingCmd(node=self.firstnode, verbose=self.verbose,
|
||||
addr=self.lastaddr, count=count, interval=0.1).run()
|
||||
(latency, mdev) = p
|
||||
self.info("latency (ms): %.03f, %.03f" % (latency, mdev))
|
||||
return p
|
||||
|
||||
def iperftest(self, time=10):
|
||||
""" Run iperf through a chain of nodes and report the maximum
|
||||
throughput.
|
||||
"""
|
||||
bps = IperfCmd(node=self.lastnode, client_node=self.firstnode,
|
||||
verbose=False, addr=self.lastaddr, time=time).run()
|
||||
self.info("throughput (bps): %s" % bps)
|
||||
return bps
|
||||
|
||||
def cputest(self, time=10, rate=512):
|
||||
""" Run MGEN through a chain of nodes and report the CPU usage and
|
||||
percent of lost packets. Rate is in kbps.
|
||||
"""
|
||||
if self.verbose:
|
||||
self.info("%s initial test ping (max 1 second)..." % \
|
||||
self.firstnode.name)
|
||||
(status, result) = self.firstnode.cmd_output(["ping", "-q", "-c", "1",
|
||||
"-w", "1", self.lastaddr])
|
||||
if status != 0:
|
||||
self.warn("initial ping from %s to %s failed! result:\n%s" % \
|
||||
(self.firstnode.name, self.lastaddr, result))
|
||||
return 0.0, 0.0
|
||||
lines = readstat()
|
||||
cpustart = getcputimes(lines[0])
|
||||
loss = MgenCmd(node=self.lastnode, client_node=self.firstnode,
|
||||
verbose=False, addr=self.lastaddr,
|
||||
time=time, rate=rate).run()
|
||||
lines = readstat()
|
||||
cpuend = getcputimes(lines[0])
|
||||
percent = calculatecpu(cpustart, cpuend)
|
||||
self.info("CPU usage (%%): %.02f, %.02f loss" % (percent, loss))
|
||||
return percent, loss
|
||||
|
||||
|
||||
def main():
|
||||
""" Main routine when running from command-line.
|
||||
"""
|
||||
usagestr = "usage: %prog [-h] [options] [args]"
|
||||
parser = optparse.OptionParser(usage=usagestr)
|
||||
parser.set_defaults(numnodes=10, delay=3, duration=10, rate=512,
|
||||
verbose=False,
|
||||
numping=50, numiperf=1, nummgen=1)
|
||||
|
||||
parser.add_option("-d", "--delay", dest="delay", type=float,
|
||||
help="wait time before testing")
|
||||
parser.add_option("-l", "--logfile", dest="logfile", type=str,
|
||||
help="log detailed output to the specified file")
|
||||
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
||||
help="number of nodes")
|
||||
parser.add_option("-r", "--rate", dest="rate", type=float,
|
||||
help="kbps rate to use for MGEN CPU tests")
|
||||
parser.add_option("--numping", dest="numping", type=int,
|
||||
help="number of ping latency test runs")
|
||||
parser.add_option("--numiperf", dest="numiperf", type=int,
|
||||
help="number of iperf throughput test runs")
|
||||
parser.add_option("--nummgen", dest="nummgen", type=int,
|
||||
help="number of MGEN CPU tests runs")
|
||||
parser.add_option("-t", "--time", dest="duration", type=int,
|
||||
help="duration in seconds of throughput and CPU tests")
|
||||
parser.add_option("-v", "--verbose", dest="verbose",
|
||||
action="store_true", help="be more verbose")
|
||||
|
||||
def usage(msg=None, err=0):
|
||||
sys.stdout.write("\n")
|
||||
if msg:
|
||||
sys.stdout.write(msg + "\n\n")
|
||||
parser.print_help()
|
||||
sys.exit(err)
|
||||
|
||||
# parse command line opt
|
||||
(opt, args) = parser.parse_args()
|
||||
|
||||
if opt.numnodes < 2:
|
||||
usage("invalid numnodes: %s" % opt.numnodes)
|
||||
if opt.delay < 0.0:
|
||||
usage("invalid delay: %s" % opt.delay)
|
||||
if opt.rate < 0.0:
|
||||
usage("invalid rate: %s" % opt.rate)
|
||||
|
||||
for a in args:
|
||||
sys.stderr.write("ignoring command line argument: %s\n" % a)
|
||||
|
||||
results = {}
|
||||
starttime = datetime.datetime.now()
|
||||
exp = Experiment(opt=opt, start=starttime)
|
||||
exp.info("Starting wlanemanetests.py tests %s" % starttime.ctime())
|
||||
|
||||
# bridged
|
||||
exp.info("setting up bridged tests 1/2 no link effects")
|
||||
exp.info("creating topology: numnodes = %s" % (opt.numnodes,))
|
||||
exp.createbridgedsession(numnodes=opt.numnodes, verbose=opt.verbose)
|
||||
exp.setnodes()
|
||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
results["0 bridged"] = exp.runalltests("bridged")
|
||||
exp.info("done; elapsed time: %s" % (datetime.datetime.now() - exp.start))
|
||||
|
||||
# bridged with netem
|
||||
exp.info("setting up bridged tests 2/2 with netem")
|
||||
exp.setneteffects(bw=54000000, delay=0)
|
||||
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
||||
results["1.0 netem"] = exp.runalltests("netem")
|
||||
exp.info("shutting down bridged session")
|
||||
|
||||
# bridged with netem (1 Mbps,200ms)
|
||||
exp.info("setting up bridged tests 3/2 with netem")
|
||||
exp.setneteffects(bw=1000000, delay=20000)
|
||||
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
||||
results["1.2 netem_1M"] = exp.runalltests("netem_1M")
|
||||
exp.info("shutting down bridged session")
|
||||
|
||||
# bridged with netem (54 kbps,500ms)
|
||||
exp.info("setting up bridged tests 3/2 with netem")
|
||||
exp.setneteffects(bw=54000, delay=100000)
|
||||
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
||||
results["1.4 netem_54K"] = exp.runalltests("netem_54K")
|
||||
exp.info("shutting down bridged session")
|
||||
exp.reset()
|
||||
|
||||
# EMANE bypass model
|
||||
exp.info("setting up EMANE tests 1/2 with bypass model")
|
||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose, cls=EmaneBypassModel, values=None)
|
||||
exp.setnodes()
|
||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
results["2.0 bypass"] = exp.runalltests("bypass")
|
||||
exp.info("shutting down bypass session")
|
||||
exp.reset()
|
||||
|
||||
exp.info("waiting %s sec (between EMANE tests)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
|
||||
# EMANE RF-PIPE model: no restrictions (max datarate)
|
||||
exp.info("setting up EMANE tests 2/4 with RF-PIPE model")
|
||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||
rfpnames = EmaneRfPipeModel.getnames()
|
||||
# max value
|
||||
rfpipevals[rfpnames.index("datarate")] = "4294967295"
|
||||
if emanever < emane.EMANE091:
|
||||
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
|
||||
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
|
||||
else:
|
||||
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
|
||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose, cls=EmaneRfPipeModel, values=rfpipevals)
|
||||
exp.setnodes()
|
||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
results["3.0 rfpipe"] = exp.runalltests("rfpipe")
|
||||
exp.info("shutting down RF-PIPE session")
|
||||
exp.reset()
|
||||
|
||||
# EMANE RF-PIPE model: 54M datarate
|
||||
exp.info("setting up EMANE tests 3/4 with RF-PIPE model 54M")
|
||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||
rfpnames = EmaneRfPipeModel.getnames()
|
||||
rfpipevals[rfpnames.index("datarate")] = "54000000"
|
||||
# TX delay != propagation delay
|
||||
# rfpipevals[ rfpnames.index("delay") ] = "5000"
|
||||
if emanever < emane.EMANE091:
|
||||
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
|
||||
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
|
||||
else:
|
||||
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
|
||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
||||
exp.setnodes()
|
||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
results["4.0 rfpipe54m"] = exp.runalltests("rfpipe54m")
|
||||
exp.info("shutting down RF-PIPE session")
|
||||
exp.reset()
|
||||
|
||||
# EMANE RF-PIPE model: 54K datarate
|
||||
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
|
||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||
rfpnames = EmaneRfPipeModel.getnames()
|
||||
rfpipevals[rfpnames.index("datarate")] = "54000"
|
||||
if emanever < emane.EMANE091:
|
||||
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
|
||||
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
|
||||
else:
|
||||
rfpipevals[rfpnames.index("propagationmodel")] = "precomputed"
|
||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
||||
exp.setnodes()
|
||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
exp.info("sending pathloss events to govern connectivity")
|
||||
exp.setpathloss(opt.numnodes)
|
||||
results["5.0 pathloss"] = exp.runalltests("pathloss")
|
||||
exp.info("shutting down RF-PIPE session")
|
||||
exp.reset()
|
||||
|
||||
# EMANE RF-PIPE model (512K, 200ms)
|
||||
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
|
||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||
rfpnames = EmaneRfPipeModel.getnames()
|
||||
rfpipevals[rfpnames.index("datarate")] = "512000"
|
||||
rfpipevals[rfpnames.index("delay")] = "200"
|
||||
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
|
||||
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
|
||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
||||
exp.setnodes()
|
||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||
time.sleep(opt.delay)
|
||||
exp.info("sending pathloss events to govern connectivity")
|
||||
exp.setpathloss(opt.numnodes)
|
||||
results["5.1 pathloss"] = exp.runalltests("pathloss")
|
||||
exp.info("shutting down RF-PIPE session")
|
||||
exp.reset()
|
||||
|
||||
# summary of results in CSV format
|
||||
exp.info("----- summary of results (%s nodes, rate=%s, duration=%s) -----" \
|
||||
% (opt.numnodes, opt.rate, opt.duration))
|
||||
exp.info("netname:latency,mdev,throughput,cpu,loss")
|
||||
|
||||
for test in sorted(results.keys()):
|
||||
(latency, mdev, throughput, cpu, loss) = results[test]
|
||||
exp.info("%s:%.03f,%.03f,%d,%.02f,%.02f" % \
|
||||
(test, latency, mdev, throughput, cpu, loss))
|
||||
|
||||
exp.logend()
|
||||
return exp
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -9,17 +9,18 @@ import optparse
|
|||
import socket
|
||||
|
||||
from core.api.tlv import coreapi
|
||||
from core.emulator.enumerations import MessageFlags, SessionTlvs, CORE_API_PORT
|
||||
from core.emulator.enumerations import CORE_API_PORT, MessageFlags, SessionTlvs
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(usage="usage: %prog [-l] <sessionid>")
|
||||
parser.add_option("-l", "--list", dest="list", action="store_true",
|
||||
help="list running sessions")
|
||||
parser.add_option(
|
||||
"-l", "--list", dest="list", action="store_true", help="list running sessions"
|
||||
)
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.list is True:
|
||||
num = '0'
|
||||
num = "0"
|
||||
flags = MessageFlags.STRING.value
|
||||
else:
|
||||
num = args[0]
|
||||
|
@ -28,7 +29,7 @@ def main():
|
|||
message = coreapi.CoreSessionMessage.pack(flags, tlvdata)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect(('localhost', CORE_API_PORT))
|
||||
sock.connect(("localhost", CORE_API_PORT))
|
||||
sock.send(message)
|
||||
|
||||
# receive and print a session list
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
configparser==3.7.4
|
||||
enum34==1.1.6
|
||||
configparser==4.0.1
|
||||
future==0.17.1
|
||||
futures==3.2.0
|
||||
grpcio==1.21.1
|
||||
grpcio==1.23.0
|
||||
grpcio-tools==1.21.1
|
||||
lxml==4.3.3
|
||||
protobuf==3.8.0
|
||||
lxml==4.4.1
|
||||
protobuf==3.9.1
|
||||
six==1.12.0
|
||||
|
|
|
@ -10,21 +10,15 @@ import logging
|
|||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from configparser import ConfigParser
|
||||
|
||||
from core import constants
|
||||
from core import load_logging_config
|
||||
from core import constants, load_logging_config
|
||||
from core.api.grpc.server import CoreGrpcServer
|
||||
from core.api.tlv.corehandlers import CoreHandler
|
||||
from core.api.tlv.corehandlers import CoreUdpHandler
|
||||
from core.api.tlv.coreserver import CoreServer
|
||||
from core.api.tlv.coreserver import CoreUdpServer
|
||||
from core.api.tlv.corehandlers import CoreHandler, CoreUdpHandler
|
||||
from core.api.tlv.coreserver import CoreServer, CoreUdpServer
|
||||
from core.emulator import enumerations
|
||||
from core.utils import close_onexec
|
||||
|
||||
load_logging_config()
|
||||
|
||||
|
||||
def banner():
|
||||
"""
|
||||
|
@ -87,7 +81,7 @@ def cored(cfg, use_ovs):
|
|||
# close handlers
|
||||
close_onexec(server.fileno())
|
||||
|
||||
logging.info("tcp/udp servers started, listening on: %s:%s", host, port)
|
||||
logging.info("CORE TLV API TCP/UDP listening on: %s:%s", host, port)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
|
@ -106,7 +100,8 @@ def get_merged_config(filename):
|
|||
"xmlfilever": "1.0",
|
||||
"numthreads": "1",
|
||||
"grpcport": "50051",
|
||||
"grpcaddress": "localhost"
|
||||
"grpcaddress": "localhost",
|
||||
"logfile": ""
|
||||
}
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
|
@ -123,6 +118,7 @@ def get_merged_config(filename):
|
|||
help="grpc port to listen on; default %s" % defaults["grpcport"])
|
||||
parser.add_argument("--grpc-address", dest="grpcaddress",
|
||||
help="grpc address to listen on; default %s" % defaults["grpcaddress"])
|
||||
parser.add_argument("-l", "--logfile", help="core logging configuration; default %s" % defaults["logfile"])
|
||||
|
||||
# parse command line options
|
||||
args = parser.parse_args()
|
||||
|
@ -134,6 +130,9 @@ def get_merged_config(filename):
|
|||
cfg = ConfigParser(defaults)
|
||||
cfg.read(filename)
|
||||
|
||||
# load logging configuration
|
||||
load_logging_config(args.logfile)
|
||||
|
||||
section = "core-daemon"
|
||||
if not cfg.has_section(section):
|
||||
cfg.add_section(section)
|
||||
|
|
|
@ -9,10 +9,12 @@ import socket
|
|||
import sys
|
||||
|
||||
from core.api.tlv import coreapi
|
||||
from core.emulator.enumerations import CORE_API_PORT
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.emulator.enumerations import SessionTlvs
|
||||
from core.emulator.enumerations import (
|
||||
CORE_API_PORT,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
SessionTlvs,
|
||||
)
|
||||
|
||||
|
||||
def print_available_tlvs(t, tlv_class):
|
||||
|
|
|
@ -1,2 +1,20 @@
|
|||
[aliases]
|
||||
test=pytest
|
||||
test=pytest
|
||||
|
||||
[isort]
|
||||
skip_glob=*_pb2*.py,utm.py,doc,build
|
||||
multi_line_output=3
|
||||
include_trailing_comma=True
|
||||
force_grid_wrap=0
|
||||
use_parentheses=True
|
||||
line_length=88
|
||||
|
||||
[flake8]
|
||||
ignore=E501,W503,E203
|
||||
max-line-length=100
|
||||
max-complexity=26
|
||||
select=B,C,E,F,W,T4
|
||||
exclude=*_pb2*.py,utm.py,doc,build
|
||||
|
||||
[tool:pytest]
|
||||
norecursedirs=distributed emane
|
||||
|
|
|
@ -23,16 +23,9 @@ def recursive_files(data_path, files_path):
|
|||
return all_files
|
||||
|
||||
|
||||
def glob_files(glob_path):
|
||||
return glob.glob(glob_path)
|
||||
|
||||
|
||||
data_files = [
|
||||
(_CORE_DIR, [
|
||||
"data/core.conf",
|
||||
"data/logging.conf",
|
||||
]),
|
||||
(_MAN_DIR, glob_files("../man/**.1")),
|
||||
(_CORE_DIR, glob.glob("data/*")),
|
||||
(_MAN_DIR, glob.glob("../man/**.1")),
|
||||
]
|
||||
data_files.extend(recursive_files(_EXAMPLES_DIR, "examples"))
|
||||
|
||||
|
@ -42,15 +35,17 @@ setup(
|
|||
packages=find_packages(),
|
||||
install_requires=[
|
||||
"configparser",
|
||||
"enum34",
|
||||
"future",
|
||||
"grpcio",
|
||||
"lxml"
|
||||
"lxml",
|
||||
"protobuf",
|
||||
],
|
||||
extra_require={
|
||||
":python_version<'3.2'": ["futures"],
|
||||
":python_version<'3.4'": ["enum34"],
|
||||
},
|
||||
tests_require=[
|
||||
"pytest",
|
||||
"pytest-runner",
|
||||
"pytest-cov",
|
||||
"mock",
|
||||
],
|
||||
data_files=data_files,
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
distributed = sys.argv[1]
|
||||
pytest.main([
|
||||
"-v",
|
||||
"--distributed", distributed,
|
||||
"--cov-report", "xml",
|
||||
"--cov=.",
|
||||
"tests"
|
||||
])
|
|
@ -16,9 +16,7 @@ from core.api.tlv.corehandlers import CoreHandler
|
|||
from core.api.tlv.coreserver import CoreServer
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes
|
||||
from core.emulator.enumerations import CORE_API_PORT, EventTlvs
|
||||
from core.emulator.enumerations import ConfigTlvs
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import CORE_API_PORT, ConfigTlvs, EventTlvs, EventTypes
|
||||
from core.nodes import ipaddress
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
||||
|
@ -30,10 +28,9 @@ class CoreServerTest(object):
|
|||
self.host = "localhost"
|
||||
self.port = port
|
||||
address = (self.host, self.port)
|
||||
self.server = CoreServer(address, CoreHandler, {
|
||||
"numthreads": 1,
|
||||
"daemonize": False,
|
||||
})
|
||||
self.server = CoreServer(
|
||||
address, CoreHandler, {"numthreads": 1, "daemonize": False}
|
||||
)
|
||||
|
||||
self.distributed_server = "core2"
|
||||
self.prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||
|
@ -65,36 +62,51 @@ class CoreServerTest(object):
|
|||
|
||||
# have broker handle a configuration state change
|
||||
self.session.set_state(EventTypes.DEFINITION_STATE)
|
||||
message = CoreEventMessage.create(0, [(EventTlvs.TYPE, EventTypes.CONFIGURATION_STATE.value)])
|
||||
message = CoreEventMessage.create(
|
||||
0, [(EventTlvs.TYPE, EventTypes.CONFIGURATION_STATE.value)]
|
||||
)
|
||||
self.request_handler.handle_message(message)
|
||||
|
||||
# add broker server for distributed core
|
||||
distributed = "%s:%s:%s" % (self.distributed_server, distributed_address, self.port)
|
||||
message = CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "broker"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
(ConfigTlvs.DATA_TYPES, (10,)),
|
||||
(ConfigTlvs.VALUES, distributed)
|
||||
])
|
||||
distributed = "%s:%s:%s" % (
|
||||
self.distributed_server,
|
||||
distributed_address,
|
||||
self.port,
|
||||
)
|
||||
message = CoreConfMessage.create(
|
||||
0,
|
||||
[
|
||||
(ConfigTlvs.OBJECT, "broker"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
(ConfigTlvs.DATA_TYPES, (10,)),
|
||||
(ConfigTlvs.VALUES, distributed),
|
||||
],
|
||||
)
|
||||
self.request_handler.handle_message(message)
|
||||
|
||||
# set session location
|
||||
message = CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "location"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
(ConfigTlvs.DATA_TYPES, (9, 9, 9, 9, 9, 9)),
|
||||
(ConfigTlvs.VALUES, "0|0| 47.5766974863|-122.125920191|0.0|150.0")
|
||||
])
|
||||
message = CoreConfMessage.create(
|
||||
0,
|
||||
[
|
||||
(ConfigTlvs.OBJECT, "location"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
(ConfigTlvs.DATA_TYPES, (9, 9, 9, 9, 9, 9)),
|
||||
(ConfigTlvs.VALUES, "0|0| 47.5766974863|-122.125920191|0.0|150.0"),
|
||||
],
|
||||
)
|
||||
self.request_handler.handle_message(message)
|
||||
|
||||
# set services for host nodes
|
||||
message = CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.SESSION, str(self.session.id)),
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
(ConfigTlvs.DATA_TYPES, (10, 10, 10)),
|
||||
(ConfigTlvs.VALUES, "host|DefaultRoute|SSH")
|
||||
])
|
||||
message = CoreConfMessage.create(
|
||||
0,
|
||||
[
|
||||
(ConfigTlvs.SESSION, str(self.session.id)),
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
(ConfigTlvs.DATA_TYPES, (10, 10, 10)),
|
||||
(ConfigTlvs.VALUES, "host|DefaultRoute|SSH"),
|
||||
],
|
||||
)
|
||||
self.request_handler.handle_message(message)
|
||||
|
||||
def shutdown(self):
|
||||
|
@ -176,11 +188,6 @@ def coreserver():
|
|||
ServiceManager.services.clear()
|
||||
|
||||
|
||||
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])
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--distributed", help="distributed server address")
|
||||
|
||||
|
|
|
@ -1,25 +1,43 @@
|
|||
"""
|
||||
Unit tests for testing CORE with distributed networks.
|
||||
"""
|
||||
from core.api.tlv.coreapi import (
|
||||
CoreConfMessage,
|
||||
CoreEventMessage,
|
||||
CoreExecMessage,
|
||||
CoreLinkMessage,
|
||||
CoreNodeMessage,
|
||||
)
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
|
||||
from core.api.tlv.coreapi import CoreExecMessage, CoreNodeMessage, CoreLinkMessage, CoreEventMessage, CoreConfMessage
|
||||
from core.emulator.enumerations import EventTypes, NodeTlvs, LinkTlvs, LinkTypes, EventTlvs, ConfigTlvs, ConfigFlags
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.ipaddress import IpAddress, MacAddress, Ipv4Prefix
|
||||
from core.emulator.enumerations import (
|
||||
ConfigFlags,
|
||||
ConfigTlvs,
|
||||
EventTlvs,
|
||||
EventTypes,
|
||||
ExecuteTlvs,
|
||||
LinkTlvs,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
NodeTlvs,
|
||||
NodeTypes,
|
||||
)
|
||||
from core.nodes.ipaddress import IpAddress, MacAddress
|
||||
|
||||
|
||||
def set_emane_model(node_id, model):
|
||||
return CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, node_id),
|
||||
(ConfigTlvs.OBJECT, model),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
])
|
||||
return CoreConfMessage.create(
|
||||
0,
|
||||
[
|
||||
(ConfigTlvs.NODE, node_id),
|
||||
(ConfigTlvs.OBJECT, model),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def node_message(_id, name, emulation_server=None, node_type=NodeTypes.DEFAULT, model=None):
|
||||
def node_message(
|
||||
_id, name, emulation_server=None, node_type=NodeTypes.DEFAULT, model=None
|
||||
):
|
||||
"""
|
||||
Convenience method for creating a node TLV messages.
|
||||
|
||||
|
@ -46,7 +64,16 @@ def node_message(_id, name, emulation_server=None, node_type=NodeTypes.DEFAULT,
|
|||
return CoreNodeMessage.create(MessageFlags.ADD.value, values)
|
||||
|
||||
|
||||
def link_message(n1, n2, intf_one=None, address_one=None, intf_two=None, address_two=None, key=None, mask=24):
|
||||
def link_message(
|
||||
n1,
|
||||
n2,
|
||||
intf_one=None,
|
||||
address_one=None,
|
||||
intf_two=None,
|
||||
address_two=None,
|
||||
key=None,
|
||||
mask=24,
|
||||
):
|
||||
"""
|
||||
Convenience method for creating link TLV messages.
|
||||
|
||||
|
@ -102,11 +129,14 @@ def command_message(node, command):
|
|||
:rtype: core.api.tlv.coreapi.CoreExecMessage
|
||||
"""
|
||||
flags = MessageFlags.STRING.value | MessageFlags.TEXT.value
|
||||
return CoreExecMessage.create(flags, [
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, command)
|
||||
])
|
||||
return CoreExecMessage.create(
|
||||
flags,
|
||||
[
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, command),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def state_message(state):
|
||||
|
@ -117,9 +147,7 @@ def state_message(state):
|
|||
:return: tlv message
|
||||
:rtype: core.api.tlv.coreapi.CoreEventMessage
|
||||
"""
|
||||
return CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, state.value)
|
||||
])
|
||||
return CoreEventMessage.create(0, [(EventTlvs.TYPE, state.value)])
|
||||
|
||||
|
||||
def validate_response(replies, _):
|
||||
|
@ -131,8 +159,8 @@ def validate_response(replies, _):
|
|||
:return: nothing
|
||||
"""
|
||||
response = replies[0]
|
||||
header = response[:CoreExecMessage.header_len]
|
||||
tlv_data = response[CoreExecMessage.header_len:]
|
||||
header = response[: CoreExecMessage.header_len]
|
||||
tlv_data = response[CoreExecMessage.header_len :]
|
||||
response = CoreExecMessage(MessageFlags.TEXT, header, tlv_data)
|
||||
assert not response.get_tlv(ExecuteTlvs.STATUS.value)
|
||||
|
||||
|
@ -149,48 +177,27 @@ class TestDistributed:
|
|||
cored.setup(distributed_address)
|
||||
|
||||
# create local node
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="host"
|
||||
)
|
||||
message = node_message(_id=1, name="n1", model="host")
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
message = node_message(
|
||||
_id=2,
|
||||
name="n2",
|
||||
emulation_server=cored.distributed_server,
|
||||
model="host"
|
||||
_id=2, name="n2", emulation_server=cored.distributed_server, model="host"
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed switch and assign to distributed server
|
||||
message = node_message(
|
||||
_id=3,
|
||||
name="n3",
|
||||
node_type=NodeTypes.SWITCH
|
||||
)
|
||||
message = node_message(_id=3, name="n3", node_type=NodeTypes.SWITCH)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
address_one=ip4_address
|
||||
)
|
||||
message = link_message(n1=1, n2=3, intf_one=0, address_one=ip4_address)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message two
|
||||
ip4_address = cored.prefix.addr(2)
|
||||
message = link_message(
|
||||
n1=3,
|
||||
n2=2,
|
||||
intf_two=0,
|
||||
address_two=ip4_address
|
||||
)
|
||||
message = link_message(n1=3, n2=2, intf_two=0, address_two=ip4_address)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
|
@ -214,31 +221,22 @@ class TestDistributed:
|
|||
cored.setup(distributed_address)
|
||||
|
||||
# configure required controlnet
|
||||
cored.session.options.set_config("controlnet", "core1:172.16.1.0/24 core2:172.16.2.0/24")
|
||||
cored.session.options.set_config(
|
||||
"controlnet", "core1:172.16.1.0/24 core2:172.16.2.0/24"
|
||||
)
|
||||
|
||||
# create local node
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="mdr"
|
||||
)
|
||||
message = node_message(_id=1, name="n1", model="mdr")
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
message = node_message(
|
||||
_id=2,
|
||||
name="n2",
|
||||
emulation_server=cored.distributed_server,
|
||||
model="mdr"
|
||||
_id=2, name="n2", emulation_server=cored.distributed_server, model="mdr"
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed switch and assign to distributed server
|
||||
message = node_message(
|
||||
_id=3,
|
||||
name="n3",
|
||||
node_type=NodeTypes.EMANE
|
||||
)
|
||||
message = node_message(_id=3, name="n3", node_type=NodeTypes.EMANE)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# set emane model
|
||||
|
@ -247,24 +245,12 @@ class TestDistributed:
|
|||
|
||||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
address_one=ip4_address,
|
||||
mask=32
|
||||
)
|
||||
message = link_message(n1=1, n2=3, intf_one=0, address_one=ip4_address, mask=32)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message two
|
||||
ip4_address = cored.prefix.addr(2)
|
||||
message = link_message(
|
||||
n1=2,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
address_one=ip4_address,
|
||||
mask=32
|
||||
)
|
||||
message = link_message(n1=2, n2=3, intf_one=0, address_one=ip4_address, mask=32)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
|
@ -288,11 +274,7 @@ class TestDistributed:
|
|||
cored.setup(distributed_address)
|
||||
|
||||
# create local node
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="host"
|
||||
)
|
||||
message = node_message(_id=1, name="n1", model="host")
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
|
@ -301,36 +283,22 @@ class TestDistributed:
|
|||
name="n2",
|
||||
emulation_server=cored.distributed_server,
|
||||
node_type=NodeTypes.PHYSICAL,
|
||||
model="prouter"
|
||||
model="prouter",
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed switch and assign to distributed server
|
||||
message = node_message(
|
||||
_id=3,
|
||||
name="n3",
|
||||
node_type=NodeTypes.SWITCH
|
||||
)
|
||||
message = node_message(_id=3, name="n3", node_type=NodeTypes.SWITCH)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
address_one=ip4_address
|
||||
)
|
||||
message = link_message(n1=1, n2=3, intf_one=0, address_one=ip4_address)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message two
|
||||
ip4_address = cored.prefix.addr(2)
|
||||
message = link_message(
|
||||
n1=3,
|
||||
n2=2,
|
||||
intf_two=0,
|
||||
address_two=ip4_address
|
||||
)
|
||||
message = link_message(n1=3, n2=2, intf_two=0, address_two=ip4_address)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
|
@ -355,11 +323,7 @@ class TestDistributed:
|
|||
cored.setup(distributed_address)
|
||||
|
||||
# create local node
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="host"
|
||||
)
|
||||
message = node_message(_id=1, name="n1", model="host")
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
|
@ -367,7 +331,7 @@ class TestDistributed:
|
|||
_id=2,
|
||||
name=distributed_address,
|
||||
emulation_server=cored.distributed_server,
|
||||
node_type=NodeTypes.TUNNEL
|
||||
node_type=NodeTypes.TUNNEL,
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
|
@ -381,7 +345,7 @@ class TestDistributed:
|
|||
address_one=ip4_address,
|
||||
intf_two=0,
|
||||
address_two=address_two,
|
||||
key=1
|
||||
key=1,
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
|
|
142
daemon/tests/emane/test_emane.py
Normal file
142
daemon/tests/emane/test_emane.py
Normal file
|
@ -0,0 +1,142 @@
|
|||
"""
|
||||
Unit tests for testing CORE EMANE networks.
|
||||
"""
|
||||
import os
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import pytest
|
||||
|
||||
from core import CoreError
|
||||
from core.emane.bypass import EmaneBypassModel
|
||||
from core.emane.commeffect import EmaneCommEffectModel
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emane.tdma import EmaneTdmaModel
|
||||
from core.emulator.emudata import NodeOptions
|
||||
|
||||
_EMANE_MODELS = [
|
||||
EmaneIeee80211abgModel,
|
||||
EmaneRfPipeModel,
|
||||
EmaneBypassModel,
|
||||
EmaneCommEffectModel,
|
||||
EmaneTdmaModel,
|
||||
]
|
||||
_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])
|
||||
|
||||
|
||||
class TestEmane:
|
||||
@pytest.mark.parametrize("model", _EMANE_MODELS)
|
||||
def test_models(self, session, model, ip_prefixes):
|
||||
"""
|
||||
Test emane models within a basic network.
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param model: emane model to test
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
|
||||
# create emane node for networking the core nodes
|
||||
emane_network = session.create_emane_network(
|
||||
model, geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
)
|
||||
emane_network.setposition(x=80, y=50)
|
||||
|
||||
# configure tdma
|
||||
if model == EmaneTdmaModel:
|
||||
session.emane.set_model_config(
|
||||
emane_network.id,
|
||||
EmaneTdmaModel.name,
|
||||
{"schedule": os.path.join(_DIR, "../examples/tdma/schedule.xml")},
|
||||
)
|
||||
|
||||
# create nodes
|
||||
node_options = NodeOptions()
|
||||
node_options.set_position(150, 150)
|
||||
node_one = session.create_wireless_node(node_options=node_options)
|
||||
node_options.set_position(300, 150)
|
||||
node_two = session.create_wireless_node(node_options=node_options)
|
||||
|
||||
for i, node in enumerate([node_one, node_two]):
|
||||
node.setposition(x=150 * (i + 1), y=150)
|
||||
interface = ip_prefixes.create_interface(node)
|
||||
session.add_link(node.id, emane_network.id, interface_one=interface)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# ping n2 from n1 and assert success
|
||||
status = ping(node_one, node_two, ip_prefixes, count=5)
|
||||
assert not status
|
||||
|
||||
def test_xml_emane(self, session, tmpdir, ip_prefixes):
|
||||
"""
|
||||
Test xml client methods for emane.
|
||||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create emane node for networking the core nodes
|
||||
emane_network = session.create_emane_network(
|
||||
EmaneIeee80211abgModel,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000),
|
||||
config={"test": "1"},
|
||||
)
|
||||
emane_network.setposition(x=80, y=50)
|
||||
|
||||
# create nodes
|
||||
node_options = NodeOptions()
|
||||
node_options.set_position(150, 150)
|
||||
node_one = session.create_wireless_node(node_options=node_options)
|
||||
node_options.set_position(300, 150)
|
||||
node_two = session.create_wireless_node(node_options=node_options)
|
||||
|
||||
for i, node in enumerate([node_one, node_two]):
|
||||
node.setposition(x=150 * (i + 1), y=150)
|
||||
interface = ip_prefixes.create_interface(node)
|
||||
session.add_link(node.id, emane_network.id, interface_one=interface)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# get ids for nodes
|
||||
emane_id = emane_network.id
|
||||
n1_id = node_one.id
|
||||
n2_id = node_two.id
|
||||
|
||||
# save xml
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
session.save_xml(file_path)
|
||||
|
||||
# verify xml file was created and can be parsed
|
||||
assert xml_file.isfile()
|
||||
assert ElementTree.parse(file_path)
|
||||
|
||||
# stop current session, clearing data
|
||||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
session.open_xml(file_path, start=True)
|
||||
|
||||
# retrieve configuration we set originally
|
||||
value = str(
|
||||
session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name)
|
||||
)
|
||||
|
||||
# verify nodes and configuration were restored
|
||||
assert session.get_node(n1_id)
|
||||
assert session.get_node(n2_id)
|
||||
assert session.get_node(emane_id)
|
||||
assert value == "1"
|
|
@ -1,2 +0,0 @@
|
|||
[pytest]
|
||||
norecursedirs = distributed
|
|
@ -1,12 +1,13 @@
|
|||
import pytest
|
||||
|
||||
from core.config import ConfigurableManager
|
||||
from core.config import ConfigurableOptions
|
||||
from core.config import Configuration
|
||||
from core.config import ModelManager
|
||||
from core.config import (
|
||||
ConfigurableManager,
|
||||
ConfigurableOptions,
|
||||
Configuration,
|
||||
ModelManager,
|
||||
)
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import ConfigDataTypes, NodeTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
|
||||
|
||||
|
@ -14,16 +15,8 @@ class TestConfigurableOptions(ConfigurableOptions):
|
|||
name_one = "value1"
|
||||
name_two = "value2"
|
||||
options = [
|
||||
Configuration(
|
||||
_id=name_one,
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label=name_one
|
||||
),
|
||||
Configuration(
|
||||
_id=name_two,
|
||||
_type=ConfigDataTypes.STRING,
|
||||
label=name_two
|
||||
)
|
||||
Configuration(_id=name_one, _type=ConfigDataTypes.STRING, label=name_one),
|
||||
Configuration(_id=name_two, _type=ConfigDataTypes.STRING, label=name_two),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -10,19 +10,13 @@ import threading
|
|||
import pytest
|
||||
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.location.mobility import Ns2ScriptedMobility
|
||||
from core.emulator.enumerations import MessageFlags, NodeTypes
|
||||
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
|
||||
]
|
||||
_WIRED = [NodeTypes.PEER_TO_PEER, NodeTypes.HUB, NodeTypes.SWITCH]
|
||||
|
||||
|
||||
def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None):
|
||||
|
@ -115,7 +109,9 @@ class TestCore:
|
|||
p, stdin, stdout, stderr = client.popen(command)
|
||||
assert not p.wait()
|
||||
assert not client.icmd(command)
|
||||
assert not client.redircmd(subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, command)
|
||||
assert not client.redircmd(
|
||||
subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, command
|
||||
)
|
||||
assert not client.shcmd(command[0])
|
||||
|
||||
# check various command using command line
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
"""
|
||||
Unit tests for testing CORE EMANE networks.
|
||||
"""
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from conftest import ping
|
||||
from core.emane.bypass import EmaneBypassModel
|
||||
from core.emane.commeffect import EmaneCommEffectModel
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emane.tdma import EmaneTdmaModel
|
||||
from core.emulator.emudata import NodeOptions
|
||||
|
||||
_EMANE_MODELS = [
|
||||
EmaneIeee80211abgModel,
|
||||
EmaneRfPipeModel,
|
||||
EmaneBypassModel,
|
||||
EmaneCommEffectModel,
|
||||
EmaneTdmaModel,
|
||||
]
|
||||
_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class TestEmane:
|
||||
@pytest.mark.parametrize("model", _EMANE_MODELS)
|
||||
def test_models(self, session, model, ip_prefixes):
|
||||
"""
|
||||
Test emane models within a basic network.
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param model: emane model to test
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
|
||||
# create emane node for networking the core nodes
|
||||
emane_network = session.create_emane_network(
|
||||
model,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
)
|
||||
emane_network.setposition(x=80, y=50)
|
||||
|
||||
# configure tdma
|
||||
if model == EmaneTdmaModel:
|
||||
session.emane.set_model_config(emane_network.id, EmaneTdmaModel.name, {
|
||||
"schedule": os.path.join(_DIR, "../examples/tdma/schedule.xml")
|
||||
})
|
||||
|
||||
# create nodes
|
||||
node_options = NodeOptions()
|
||||
node_options.set_position(150, 150)
|
||||
node_one = session.create_wireless_node(node_options=node_options)
|
||||
node_options.set_position(300, 150)
|
||||
node_two = session.create_wireless_node(node_options=node_options)
|
||||
|
||||
for i, node in enumerate([node_one, node_two]):
|
||||
node.setposition(x=150 * (i + 1), y=150)
|
||||
interface = ip_prefixes.create_interface(node)
|
||||
session.add_link(node.id, emane_network.id, interface_one=interface)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# ping n2 from n1 and assert success
|
||||
status = ping(node_one, node_two, ip_prefixes, count=5)
|
||||
assert not status
|
|
@ -1,17 +1,23 @@
|
|||
import time
|
||||
|
||||
import grpc
|
||||
import pytest
|
||||
from builtins import int
|
||||
from queue import Queue
|
||||
|
||||
import grpc
|
||||
import pytest
|
||||
|
||||
from core import CoreError
|
||||
from core.api.grpc import core_pb2
|
||||
from core.api.grpc.client import CoreGrpcClient
|
||||
from core.config import ConfigShim
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.data import EventData
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes, EventTypes, ConfigFlags, ExceptionLevels
|
||||
from core.emulator.enumerations import (
|
||||
ConfigFlags,
|
||||
EventTypes,
|
||||
ExceptionLevels,
|
||||
NodeTypes,
|
||||
)
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
|
||||
|
||||
|
@ -35,10 +41,7 @@ class TestGrpc:
|
|||
assert response.session_id == session_id
|
||||
assert session.id == session_id
|
||||
|
||||
@pytest.mark.parametrize("session_id, expected", [
|
||||
(None, True),
|
||||
(6013, False)
|
||||
])
|
||||
@pytest.mark.parametrize("session_id, expected", [(None, True), (6013, False)])
|
||||
def test_delete_session(self, grpc_server, session_id, expected):
|
||||
# given
|
||||
client = CoreGrpcClient()
|
||||
|
@ -130,9 +133,13 @@ class TestGrpc:
|
|||
with client.context_connect():
|
||||
response = client.set_session_location(
|
||||
session.id,
|
||||
x=xyz[0], y=xyz[1], z=xyz[2],
|
||||
lat=lat_lon_alt[0], lon=lat_lon_alt[1], alt=lat_lon_alt[2],
|
||||
scale=scale
|
||||
x=xyz[0],
|
||||
y=xyz[1],
|
||||
z=xyz[2],
|
||||
lat=lat_lon_alt[0],
|
||||
lon=lat_lon_alt[1],
|
||||
alt=lat_lon_alt[2],
|
||||
scale=scale,
|
||||
)
|
||||
|
||||
# then
|
||||
|
@ -165,7 +172,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_session_state(session.id, core_pb2.SessionState.DEFINITION)
|
||||
response = client.set_session_state(
|
||||
session.id, core_pb2.SessionState.DEFINITION
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -198,10 +207,7 @@ class TestGrpc:
|
|||
# then
|
||||
assert response.node.id == node.id
|
||||
|
||||
@pytest.mark.parametrize("node_id, expected", [
|
||||
(1, True),
|
||||
(2, False)
|
||||
])
|
||||
@pytest.mark.parametrize("node_id, expected", [(1, True), (2, False)])
|
||||
def test_edit_node(self, grpc_server, node_id, expected):
|
||||
# given
|
||||
client = CoreGrpcClient()
|
||||
|
@ -220,10 +226,7 @@ class TestGrpc:
|
|||
assert node.position.x == x
|
||||
assert node.position.y == y
|
||||
|
||||
@pytest.mark.parametrize("node_id, expected", [
|
||||
(1, True),
|
||||
(2, False)
|
||||
])
|
||||
@pytest.mark.parametrize("node_id, expected", [(1, True), (2, False)])
|
||||
def test_delete_node(self, grpc_server, node_id, expected):
|
||||
# given
|
||||
client = CoreGrpcClient()
|
||||
|
@ -237,7 +240,7 @@ class TestGrpc:
|
|||
# then
|
||||
assert response.result is expected
|
||||
if expected is True:
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert session.get_node(node.id)
|
||||
|
||||
def test_node_command(self, grpc_server):
|
||||
|
@ -302,7 +305,9 @@ class TestGrpc:
|
|||
file_name = "test"
|
||||
file_data = "echo hello"
|
||||
with client.context_connect():
|
||||
response = client.add_hook(session.id, core_pb2.SessionState.RUNTIME, file_name, file_data)
|
||||
response = client.add_hook(
|
||||
session.id, core_pb2.SessionState.RUNTIME, file_name, file_data
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -408,7 +413,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.edit_link(session.id, node.id, switch.id, options, interface_one_id=interface.id)
|
||||
response = client.edit_link(
|
||||
session.id, node.id, switch.id, options, interface_one_id=interface.id
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -435,7 +442,8 @@ class TestGrpc:
|
|||
# then
|
||||
with client.context_connect():
|
||||
response = client.delete_link(
|
||||
session.id, node_one.id, node_two.id, interface_one.id, interface_two.id)
|
||||
session.id, node_one.id, node_two.id, interface_one.id, interface_two.id
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -467,14 +475,18 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_wlan_config(session.id, wlan.id, {
|
||||
range_key: range_value,
|
||||
"delay": "0",
|
||||
"loss": "0",
|
||||
"bandwidth": "50000",
|
||||
"error": "0",
|
||||
"jitter": "0"
|
||||
})
|
||||
response = client.set_wlan_config(
|
||||
session.id,
|
||||
wlan.id,
|
||||
{
|
||||
range_key: range_value,
|
||||
"delay": "0",
|
||||
"loss": "0",
|
||||
"bandwidth": "50000",
|
||||
"error": "0",
|
||||
"jitter": "0",
|
||||
},
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -516,12 +528,13 @@ class TestGrpc:
|
|||
client = CoreGrpcClient()
|
||||
session = grpc_server.coreemu.create_session()
|
||||
emane_network = session.create_emane_network(
|
||||
model=EmaneIeee80211abgModel,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
)
|
||||
config_key = "platform_id_start"
|
||||
config_value = "2"
|
||||
session.emane.set_model_config(emane_network.id, EmaneIeee80211abgModel.name, {config_key: config_value})
|
||||
session.emane.set_model_config(
|
||||
emane_network.id, EmaneIeee80211abgModel.name, {config_key: config_value}
|
||||
)
|
||||
|
||||
# then
|
||||
with client.context_connect():
|
||||
|
@ -536,8 +549,7 @@ class TestGrpc:
|
|||
client = CoreGrpcClient()
|
||||
session = grpc_server.coreemu.create_session()
|
||||
emane_network = session.create_emane_network(
|
||||
model=EmaneIeee80211abgModel,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
)
|
||||
config_key = "bandwidth"
|
||||
config_value = "900000"
|
||||
|
@ -545,11 +557,17 @@ class TestGrpc:
|
|||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_emane_model_config(
|
||||
session.id, emane_network.id, EmaneIeee80211abgModel.name, {config_key: config_value})
|
||||
session.id,
|
||||
emane_network.id,
|
||||
EmaneIeee80211abgModel.name,
|
||||
{config_key: config_value},
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
config = session.emane.get_model_config(emane_network.id, EmaneIeee80211abgModel.name)
|
||||
config = session.emane.get_model_config(
|
||||
emane_network.id, EmaneIeee80211abgModel.name
|
||||
)
|
||||
assert config[config_key] == config_value
|
||||
|
||||
def test_get_emane_model_config(self, grpc_server):
|
||||
|
@ -557,14 +575,14 @@ class TestGrpc:
|
|||
client = CoreGrpcClient()
|
||||
session = grpc_server.coreemu.create_session()
|
||||
emane_network = session.create_emane_network(
|
||||
model=EmaneIeee80211abgModel,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000)
|
||||
)
|
||||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.get_emane_model_config(
|
||||
session.id, emane_network.id, EmaneIeee80211abgModel.name)
|
||||
session.id, emane_network.id, EmaneIeee80211abgModel.name
|
||||
)
|
||||
|
||||
# then
|
||||
assert len(response.groups) > 0
|
||||
|
@ -620,7 +638,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_mobility_config(session.id, wlan.id, {config_key: config_value})
|
||||
response = client.set_mobility_config(
|
||||
session.id, wlan.id, {config_key: config_value}
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -637,7 +657,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.mobility_action(session.id, wlan.id, core_pb2.MobilityAction.STOP)
|
||||
response = client.mobility_action(
|
||||
session.id, wlan.id, core_pb2.MobilityAction.STOP
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -701,7 +723,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.get_node_service_file(session.id, node.id, "DefaultRoute", "defaultroute.sh")
|
||||
response = client.get_node_service_file(
|
||||
session.id, node.id, "DefaultRoute", "defaultroute.sh"
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.data is not None
|
||||
|
@ -716,11 +740,15 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_node_service(session.id, node.id, service_name, [], validate, [])
|
||||
response = client.set_node_service(
|
||||
session.id, node.id, service_name, [], validate, []
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
service = session.services.get_service(node.id, service_name, default_service=True)
|
||||
service = session.services.get_service(
|
||||
node.id, service_name, default_service=True
|
||||
)
|
||||
assert service.validate == tuple(validate)
|
||||
|
||||
def test_set_node_service_file(self, grpc_server):
|
||||
|
@ -734,7 +762,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_node_service_file(session.id, node.id, service_name, file_name, file_data)
|
||||
response = client.set_node_service_file(
|
||||
session.id, node.id, service_name, file_name, file_data
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -750,7 +780,9 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.service_action(session.id, node.id, service_name, core_pb2.ServiceAction.STOP)
|
||||
response = client.service_action(
|
||||
session.id, node.id, service_name, core_pb2.ServiceAction.STOP
|
||||
)
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
|
@ -803,7 +835,7 @@ class TestGrpc:
|
|||
def test_throughputs(self, grpc_server):
|
||||
# given
|
||||
client = CoreGrpcClient()
|
||||
session = grpc_server.coreemu.create_session()
|
||||
grpc_server.coreemu.create_session()
|
||||
queue = Queue()
|
||||
|
||||
def handle_event(event_data):
|
||||
|
@ -831,7 +863,9 @@ class TestGrpc:
|
|||
with client.context_connect():
|
||||
client.events(session.id, handle_event)
|
||||
time.sleep(0.1)
|
||||
event = EventData(event_type=EventTypes.RUNTIME_STATE.value, time="%s" % time.time())
|
||||
event = EventData(
|
||||
event_type=EventTypes.RUNTIME_STATE.value, time="%s" % time.time()
|
||||
)
|
||||
session.broadcast_event(event)
|
||||
|
||||
# then
|
||||
|
@ -852,7 +886,9 @@ class TestGrpc:
|
|||
client.events(session.id, handle_event)
|
||||
time.sleep(0.1)
|
||||
session_config = session.options.get_configs()
|
||||
config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, session.options, session_config)
|
||||
config_data = ConfigShim.config_data(
|
||||
0, None, ConfigFlags.UPDATE.value, session.options, session_config
|
||||
)
|
||||
session.broadcast_config(config_data)
|
||||
|
||||
# then
|
||||
|
@ -892,7 +928,9 @@ class TestGrpc:
|
|||
with client.context_connect():
|
||||
client.events(session.id, handle_event)
|
||||
time.sleep(0.1)
|
||||
file_data = session.services.get_service_file(node, "DefaultRoute", "defaultroute.sh")
|
||||
file_data = session.services.get_service_file(
|
||||
node, "DefaultRoute", "defaultroute.sh"
|
||||
)
|
||||
session.broadcast_file(file_data)
|
||||
|
||||
# then
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
from core.emulator.emudata import LinkOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core import utils
|
||||
|
||||
|
||||
def create_ptp_network(session, ip_prefixes):
|
||||
|
@ -19,35 +18,20 @@ def create_ptp_network(session, ip_prefixes):
|
|||
return node_one, node_two
|
||||
|
||||
|
||||
def ping_output(from_node, to_node, ip_prefixes):
|
||||
address = ip_prefixes.ip4_address(to_node)
|
||||
output = from_node.check_cmd(["ping", "-i", "0.05", "-c", "3", address])
|
||||
return output
|
||||
|
||||
|
||||
def iperf(from_node, to_node, ip_prefixes):
|
||||
# run iperf server, run client, kill iperf server
|
||||
address = ip_prefixes.ip4_address(to_node)
|
||||
vcmd, stdin, stdout, stderr = to_node.client.popen(["iperf", "-s", "-u", "-y", "C"])
|
||||
from_node.cmd(["iperf", "-u", "-t", "5", "-c", address])
|
||||
to_node.cmd(["killall", "-9", "iperf"])
|
||||
return stdout.read().decode("utf-8").strip()
|
||||
|
||||
|
||||
class TestLinks:
|
||||
def test_ptp(self, session, ip_prefixes):
|
||||
# given
|
||||
node_one = session.add_node()
|
||||
node_two = session.add_node()
|
||||
interface_one = ip_prefixes.create_interface(node_one)
|
||||
inteface_two = ip_prefixes.create_interface(node_two)
|
||||
interface_two = ip_prefixes.create_interface(node_two)
|
||||
|
||||
# when
|
||||
session.add_link(node_one.id, node_two.id, interface_one, inteface_two)
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
|
||||
|
||||
# then
|
||||
assert node_one.netif(interface_one.id)
|
||||
assert node_two.netif(inteface_two.id)
|
||||
assert node_two.netif(interface_two.id)
|
||||
|
||||
def test_node_to_net(self, session, ip_prefixes):
|
||||
# given
|
||||
|
@ -88,32 +72,42 @@ class TestLinks:
|
|||
|
||||
def test_link_update(self, session, ip_prefixes):
|
||||
# given
|
||||
delay = 50
|
||||
bandwidth = 5000000
|
||||
per = 25
|
||||
dup = 25
|
||||
jitter = 10
|
||||
node_one = session.add_node()
|
||||
node_two = session.add_node(_type=NodeTypes.SWITCH)
|
||||
interface_one = ip_prefixes.create_interface(node_one)
|
||||
session.add_link(node_one.id, node_two.id, interface_one)
|
||||
interface = node_one.netif(interface_one.id)
|
||||
output = utils.check_cmd(["tc", "qdisc", "show", "dev", interface.localname])
|
||||
assert "delay" not in output
|
||||
assert "rate" not in output
|
||||
assert "loss" not in output
|
||||
assert "duplicate" not in output
|
||||
interface_one_data = ip_prefixes.create_interface(node_one)
|
||||
session.add_link(node_one.id, node_two.id, interface_one_data)
|
||||
interface_one = node_one.netif(interface_one_data.id)
|
||||
assert interface_one.getparam("delay") != delay
|
||||
assert interface_one.getparam("bw") != bandwidth
|
||||
assert interface_one.getparam("loss") != per
|
||||
assert interface_one.getparam("duplicate") != dup
|
||||
assert interface_one.getparam("jitter") != jitter
|
||||
|
||||
# when
|
||||
link_options = LinkOptions()
|
||||
link_options.delay = 50
|
||||
link_options.bandwidth = 5000000
|
||||
link_options.per = 25
|
||||
link_options.dup = 25
|
||||
session.update_link(node_one.id, node_two.id,
|
||||
interface_one_id=interface_one.id, link_options=link_options)
|
||||
link_options.delay = delay
|
||||
link_options.bandwidth = bandwidth
|
||||
link_options.per = per
|
||||
link_options.dup = dup
|
||||
link_options.jitter = jitter
|
||||
session.update_link(
|
||||
node_one.id,
|
||||
node_two.id,
|
||||
interface_one_id=interface_one_data.id,
|
||||
link_options=link_options,
|
||||
)
|
||||
|
||||
# then
|
||||
output = utils.check_cmd(["tc", "qdisc", "show", "dev", interface.localname])
|
||||
assert "delay" in output
|
||||
assert "rate" in output
|
||||
assert "loss" in output
|
||||
assert "duplicate" in output
|
||||
assert interface_one.getparam("delay") == delay
|
||||
assert interface_one.getparam("bw") == bandwidth
|
||||
assert interface_one.getparam("loss") == per
|
||||
assert interface_one.getparam("duplicate") == dup
|
||||
assert interface_one.getparam("jitter") == jitter
|
||||
|
||||
def test_link_delete(self, session, ip_prefixes):
|
||||
# given
|
||||
|
@ -126,133 +120,10 @@ class TestLinks:
|
|||
assert node_two.netif(interface_two.id)
|
||||
|
||||
# when
|
||||
session.delete_link(node_one.id, node_two.id, interface_one.id, interface_two.id)
|
||||
session.delete_link(
|
||||
node_one.id, node_two.id, interface_one.id, interface_two.id
|
||||
)
|
||||
|
||||
# then
|
||||
assert not node_one.netif(interface_one.id)
|
||||
assert not node_two.netif(interface_two.id)
|
||||
|
||||
def test_link_bandwidth(self, session, ip_prefixes):
|
||||
"""
|
||||
Test ptp node network with modifying link bandwidth.
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
|
||||
# create link network
|
||||
node_one, node_two = create_ptp_network(session, ip_prefixes)
|
||||
|
||||
# output csv index
|
||||
bandwidth_index = 8
|
||||
|
||||
# run iperf, validate normal bandwidth
|
||||
stdout = iperf(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
value = int(stdout.split(',')[bandwidth_index])
|
||||
assert 900000 <= value <= 1100000
|
||||
|
||||
# change bandwidth in bits per second
|
||||
link_options = LinkOptions()
|
||||
link_options.bandwidth = 500000
|
||||
session.update_link(node_one.id, node_two.id, link_options=link_options)
|
||||
|
||||
# run iperf again
|
||||
stdout = iperf(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
value = int(stdout.split(',')[bandwidth_index])
|
||||
assert 400000 <= value <= 600000
|
||||
|
||||
def test_link_loss(self, session, ip_prefixes):
|
||||
"""
|
||||
Test ptp node network with modifying link packet loss.
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
|
||||
# create link network
|
||||
node_one, node_two = create_ptp_network(session, ip_prefixes)
|
||||
|
||||
# output csv index
|
||||
loss_index = -2
|
||||
|
||||
# run iperf, validate normal bandwidth
|
||||
stdout = iperf(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
value = float(stdout.split(',')[loss_index])
|
||||
assert 0 <= value <= 0.5
|
||||
|
||||
# change bandwidth in bits per second
|
||||
link_options = LinkOptions()
|
||||
link_options.per = 50
|
||||
session.update_link(node_one.id, node_two.id, link_options=link_options)
|
||||
|
||||
# run iperf again
|
||||
stdout = iperf(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
value = float(stdout.split(',')[loss_index])
|
||||
assert 40 <= value <= 60
|
||||
|
||||
def test_link_delay(self, session, ip_prefixes):
|
||||
"""
|
||||
Test ptp node network with modifying link packet delay.
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
|
||||
# create link network
|
||||
node_one, node_two = create_ptp_network(session, ip_prefixes)
|
||||
|
||||
# run ping for delay information
|
||||
stdout = ping_output(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
rtt_line = stdout.split("\n")[-1]
|
||||
rtt_values = rtt_line.split("=")[1].split("ms")[0].strip()
|
||||
rtt_avg = float(rtt_values.split("/")[2])
|
||||
assert 0 <= rtt_avg <= 0.2
|
||||
|
||||
# change delay in microseconds
|
||||
link_options = LinkOptions()
|
||||
link_options.delay = 1000000
|
||||
session.update_link(node_one.id, node_two.id, link_options=link_options)
|
||||
|
||||
# run ping for delay information again
|
||||
stdout = ping_output(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
rtt_line = stdout.split("\n")[-1]
|
||||
rtt_values = rtt_line.split("=")[1].split("ms")[0].strip()
|
||||
rtt_avg = float(rtt_values.split("/")[2])
|
||||
assert 1800 <= rtt_avg <= 2200
|
||||
|
||||
def test_link_jitter(self, session, ip_prefixes):
|
||||
"""
|
||||
Test ptp node network with modifying link packet jitter.
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
|
||||
# create link network
|
||||
node_one, node_two = create_ptp_network(session, ip_prefixes)
|
||||
|
||||
# output csv index
|
||||
jitter_index = 9
|
||||
|
||||
# run iperf
|
||||
stdout = iperf(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
value = float(stdout.split(",")[jitter_index])
|
||||
assert -0.5 <= value <= 0.05
|
||||
|
||||
# change jitter in microseconds
|
||||
link_options = LinkOptions()
|
||||
link_options.jitter = 1000000
|
||||
session.update_link(node_one.id, node_two.id, link_options=link_options)
|
||||
|
||||
# run iperf again
|
||||
stdout = iperf(node_one, node_two, ip_prefixes)
|
||||
assert stdout
|
||||
value = float(stdout.split(",")[jitter_index])
|
||||
assert 200 <= value <= 500
|
||||
|
|
|
@ -3,22 +3,13 @@ import time
|
|||
|
||||
import pytest
|
||||
|
||||
from core import CoreError, utils
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core import utils
|
||||
|
||||
MODELS = [
|
||||
"router",
|
||||
"host",
|
||||
"PC",
|
||||
"mdr",
|
||||
]
|
||||
MODELS = ["router", "host", "PC", "mdr"]
|
||||
|
||||
NET_TYPES = [
|
||||
NodeTypes.SWITCH,
|
||||
NodeTypes.HUB,
|
||||
NodeTypes.WIRELESS_LAN
|
||||
]
|
||||
NET_TYPES = [NodeTypes.SWITCH, NodeTypes.HUB, NodeTypes.WIRELESS_LAN]
|
||||
|
||||
|
||||
class TestNodes:
|
||||
|
@ -62,7 +53,7 @@ class TestNodes:
|
|||
session.delete_node(node.id)
|
||||
|
||||
# then
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
session.get_node(node.id)
|
||||
|
||||
@pytest.mark.parametrize("net_type", NET_TYPES)
|
||||
|
|
|
@ -2,9 +2,7 @@ import os
|
|||
|
||||
import pytest
|
||||
|
||||
from core.services.coreservices import CoreService
|
||||
from core.services.coreservices import ServiceDependencies
|
||||
from core.services.coreservices import ServiceManager
|
||||
from core.services.coreservices import CoreService, ServiceDependencies, ServiceManager
|
||||
|
||||
_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
||||
|
@ -205,8 +203,12 @@ class TestServices:
|
|||
file_name = my_service.configs[0]
|
||||
file_data_one = "# custom file one"
|
||||
file_data_two = "# custom file two"
|
||||
session.services.set_service_file(node_one.id, my_service.name, file_name, file_data_one)
|
||||
session.services.set_service_file(node_two.id, my_service.name, file_name, file_data_two)
|
||||
session.services.set_service_file(
|
||||
node_one.id, my_service.name, file_name, file_data_one
|
||||
)
|
||||
session.services.set_service_file(
|
||||
node_two.id, my_service.name, file_name, file_data_two
|
||||
)
|
||||
|
||||
# when
|
||||
custom_service_one = session.services.get_service(node_one.id, my_service.name)
|
||||
|
@ -241,9 +243,13 @@ class TestServices:
|
|||
|
||||
# when
|
||||
no_service = session.services.get_service(node.id, SERVICE_ONE)
|
||||
default_service = session.services.get_service(node.id, SERVICE_ONE, default_service=True)
|
||||
default_service = session.services.get_service(
|
||||
node.id, SERVICE_ONE, default_service=True
|
||||
)
|
||||
session.services.set_service(node.id, SERVICE_ONE)
|
||||
custom_service = session.services.get_service(node.id, SERVICE_ONE, default_service=True)
|
||||
custom_service = session.services.get_service(
|
||||
node.id, SERVICE_ONE, default_service=True
|
||||
)
|
||||
|
||||
# then
|
||||
assert no_service is None
|
||||
|
@ -252,13 +258,7 @@ class TestServices:
|
|||
|
||||
def test_services_dependencies(self):
|
||||
# given
|
||||
services = [
|
||||
ServiceA,
|
||||
ServiceB,
|
||||
ServiceC,
|
||||
ServiceD,
|
||||
ServiceF
|
||||
]
|
||||
services = [ServiceA, ServiceB, ServiceC, ServiceD, ServiceF]
|
||||
|
||||
# when
|
||||
boot_paths = ServiceDependencies(services).boot_paths()
|
||||
|
@ -274,7 +274,7 @@ class TestServices:
|
|||
ServiceC,
|
||||
ServiceD,
|
||||
ServiceF,
|
||||
ServiceBadDependency
|
||||
ServiceBadDependency,
|
||||
]
|
||||
|
||||
# when, then
|
||||
|
@ -285,13 +285,7 @@ class TestServices:
|
|||
# given
|
||||
service_d = ServiceD()
|
||||
service_d.dependencies = ("C",)
|
||||
services = [
|
||||
ServiceA,
|
||||
ServiceB,
|
||||
ServiceC,
|
||||
service_d,
|
||||
ServiceF
|
||||
]
|
||||
services = [ServiceA, ServiceB, ServiceC, service_d, ServiceF]
|
||||
|
||||
# when, then
|
||||
with pytest.raises(ValueError):
|
||||
|
|
|
@ -2,8 +2,8 @@ from xml.etree import ElementTree
|
|||
|
||||
import pytest
|
||||
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.emudata import NodeOptions, LinkOptions
|
||||
from core import CoreError
|
||||
from core.emulator.emudata import LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.services.utility import SshService
|
||||
|
@ -84,9 +84,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
@ -121,7 +121,9 @@ class TestXml:
|
|||
session.services.set_service(node_one.id, SshService.name)
|
||||
service_file = SshService.configs[0]
|
||||
file_data = "# test"
|
||||
session.services.set_service_file(node_one.id, SshService.name, service_file, file_data)
|
||||
session.services.set_service_file(
|
||||
node_one.id, SshService.name, service_file, file_data
|
||||
)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
@ -143,9 +145,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
@ -203,9 +205,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
@ -220,72 +222,6 @@ class TestXml:
|
|||
assert session.get_node(wlan_id)
|
||||
assert value == "1"
|
||||
|
||||
def test_xml_emane(self, session, tmpdir, ip_prefixes):
|
||||
"""
|
||||
Test xml client methods for emane.
|
||||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create emane node for networking the core nodes
|
||||
emane_network = session.create_emane_network(
|
||||
EmaneIeee80211abgModel,
|
||||
geo_reference=(47.57917, -122.13232, 2.00000),
|
||||
config={"test": "1"}
|
||||
)
|
||||
emane_network.setposition(x=80, y=50)
|
||||
|
||||
# create nodes
|
||||
node_options = NodeOptions()
|
||||
node_options.set_position(150, 150)
|
||||
node_one = session.create_wireless_node(node_options=node_options)
|
||||
node_options.set_position(300, 150)
|
||||
node_two = session.create_wireless_node(node_options=node_options)
|
||||
|
||||
for i, node in enumerate([node_one, node_two]):
|
||||
node.setposition(x=150 * (i + 1), y=150)
|
||||
interface = ip_prefixes.create_interface(node)
|
||||
session.add_link(node.id, emane_network.id, interface_one=interface)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# get ids for nodes
|
||||
emane_id = emane_network.id
|
||||
n1_id = node_one.id
|
||||
n2_id = node_two.id
|
||||
|
||||
# save xml
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
session.save_xml(file_path)
|
||||
|
||||
# verify xml file was created and can be parsed
|
||||
assert xml_file.isfile()
|
||||
assert ElementTree.parse(file_path)
|
||||
|
||||
# stop current session, clearing data
|
||||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
session.open_xml(file_path, start=True)
|
||||
|
||||
# retrieve configuration we set originally
|
||||
value = str(session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name))
|
||||
|
||||
# verify nodes and configuration were restored
|
||||
assert session.get_node(n1_id)
|
||||
assert session.get_node(n2_id)
|
||||
assert session.get_node(emane_id)
|
||||
assert value == "1"
|
||||
|
||||
def test_network_to_network(self, session, tmpdir):
|
||||
"""
|
||||
Test xml generation when dealing with network to network nodes.
|
||||
|
@ -320,9 +256,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
@ -355,7 +291,9 @@ class TestXml:
|
|||
link_options.jitter = 10
|
||||
link_options.delay = 30
|
||||
link_options.dup = 5
|
||||
session.add_link(node_one.id, switch.id, interface_one, link_options=link_options)
|
||||
session.add_link(
|
||||
node_one.id, switch.id, interface_one, link_options=link_options
|
||||
)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
@ -377,9 +315,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
@ -420,7 +358,9 @@ class TestXml:
|
|||
link_options.jitter = 10
|
||||
link_options.delay = 30
|
||||
link_options.dup = 5
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two, link_options)
|
||||
session.add_link(
|
||||
node_one.id, node_two.id, interface_one, interface_two, link_options
|
||||
)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
@ -442,9 +382,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
@ -486,7 +426,9 @@ class TestXml:
|
|||
link_options_one.per = 10.5
|
||||
link_options_one.dup = 5
|
||||
link_options_one.jitter = 5
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two, link_options_one)
|
||||
session.add_link(
|
||||
node_one.id, node_two.id, interface_one, interface_two, link_options_one
|
||||
)
|
||||
link_options_two = LinkOptions()
|
||||
link_options_two.unidirectional = 1
|
||||
link_options_two.bandwidth = 10000
|
||||
|
@ -494,7 +436,13 @@ class TestXml:
|
|||
link_options_two.per = 10
|
||||
link_options_two.dup = 10
|
||||
link_options_two.jitter = 10
|
||||
session.update_link(node_two.id, node_one.id, interface_two.id, interface_one.id, link_options_two)
|
||||
session.update_link(
|
||||
node_two.id,
|
||||
node_one.id,
|
||||
interface_two.id,
|
||||
interface_one.id,
|
||||
link_options_two,
|
||||
)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
@ -516,9 +464,9 @@ class TestXml:
|
|||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
with pytest.raises(CoreError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
|
|
121
docs/devguide.md
121
docs/devguide.md
|
@ -3,44 +3,115 @@
|
|||
* Table of Contents
|
||||
{:toc}
|
||||
|
||||
## Source Code Guide
|
||||
## Repository Overview
|
||||
|
||||
The CORE source consists of several different programming languages for historical reasons. Current development focuses on the Python modules and daemon. Here is a brief description of the source directories.
|
||||
The CORE source consists of several different programming languages for historical reasons.
|
||||
Current development focuses on the Python modules and daemon. Here is a brief description of the source directories.
|
||||
|
||||
These are being actively developed as of CORE 5.1:
|
||||
| Directory | Description |
|
||||
|---|---|
|
||||
|corefx|JavaFX based GUI using gRPC API to replace legacy GUI|
|
||||
|daemon|Python CORE daemon code that handles receiving API calls and creating containers|
|
||||
|docs|Markdown Documentation currently hosted on GitHub|
|
||||
|gui|Tcl/Tk GUI|
|
||||
|man|Template files for creating man pages for various CORE command line utilities|
|
||||
|netns|Python C extension modules for creating CORE containers|
|
||||
|ns3|Experimental python ns3 script support for running CORE|
|
||||
|scripts|Template files used for running CORE as a service|
|
||||
|
||||
* *gui* - Tcl/Tk GUI. This uses Tcl/Tk because of its roots with the IMUNES
|
||||
project.
|
||||
* *daemon* - Python modules are found in the :file:`daemon/core` directory, the
|
||||
daemon under :file:`daemon/scripts/core-daemon`
|
||||
* *netns* - Python extension modules for Linux Network Namespace support are in :file:`netns`.
|
||||
* *doc* - Documentation for the manual lives here in reStructuredText format.
|
||||
## Getting started
|
||||
|
||||
Not actively being developed:
|
||||
Overview for setting up the pipenv environment, building core, installing the GUI and netns, then running
|
||||
the core-daemon for development.
|
||||
|
||||
* *ns3* - Python ns3 script support for running CORE.
|
||||
### Setup Python Environment
|
||||
|
||||
To leverage the dev environment you need python 3.6+.
|
||||
|
||||
```shell
|
||||
# change to daemon directory
|
||||
cd $REPO/daemon
|
||||
|
||||
# install pipenv
|
||||
pip3 install pipenv
|
||||
|
||||
# setup a virtual environment and install all required development dependencies
|
||||
python3 -m pipenv install --dev
|
||||
|
||||
# setup python variable using pipenv created python
|
||||
export PYTHON=$(python3 -m pipenv --py)
|
||||
```
|
||||
|
||||
### Build CORE
|
||||
|
||||
```shell
|
||||
# clone core
|
||||
git clone https://github.com/coreemu/core.git
|
||||
cd core
|
||||
|
||||
# build core
|
||||
./bootstrap.sh
|
||||
./configure --prefix=/usr
|
||||
make
|
||||
```
|
||||
|
||||
### Install netns and GUI
|
||||
|
||||
Below commands assume your development will be focused on the daemon.
|
||||
|
||||
```shell
|
||||
# install GUI
|
||||
cd $REPO/gui
|
||||
sudo make install
|
||||
|
||||
# install netns scripts
|
||||
cd $REPO/netns
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Running CORE
|
||||
|
||||
This will run the core-daemon server using the configuration files within the repo.
|
||||
|
||||
```shell
|
||||
python3 -m pipenv run coredev
|
||||
```
|
||||
|
||||
## The CORE API
|
||||
|
||||
The CORE API is used between different components of CORE for communication. The GUI communicates with the CORE daemon using the API. One emulation server communicates with another using the API. The API also allows other systems to interact with the CORE emulation. The API allows another system to add, remove, or modify nodes and links, and enables executing commands on the emulated systems. Wireless link parameters are updated on-the-fly based on node positions.
|
||||
The CORE API is used between different components of CORE for communication. The GUI communicates with the CORE daemon
|
||||
using the API. One emulation server communicates with another using the API. The API also allows other systems to
|
||||
interact with the CORE emulation. The API allows another system to add, remove, or modify nodes and links, and enables
|
||||
executing commands on the emulated systems. Wireless link parameters are updated on-the-fly based on node positions.
|
||||
|
||||
CORE listens on a local TCP port for API messages. The other system could be software running locally or another machine accessible across the network.
|
||||
CORE listens on a local TCP port for API messages. The other system could be software running locally or another
|
||||
machine accessible across the network.
|
||||
|
||||
The CORE API is currently specified in a separate document, available from the CORE website.
|
||||
|
||||
## Linux network namespace Commands
|
||||
## Linux Network Namespace Commands
|
||||
|
||||
Linux network namespace containers are often managed using the *Linux Container Tools* or *lxc-tools* package. The lxc-tools website is available here http://lxc.sourceforge.net/ for more information. CORE does not use these management utilities, but includes its own set of tools for instantiating and configuring network namespace containers. This section describes these tools.
|
||||
Linux network namespace containers are often managed using the *Linux Container Tools* or *lxc-tools* package.
|
||||
The lxc-tools website is available here http://lxc.sourceforge.net/ for more information. CORE does not use these
|
||||
management utilities, but includes its own set of tools for instantiating and configuring network namespace containers.
|
||||
This section describes these tools.
|
||||
|
||||
### vnoded command
|
||||
### vnoded
|
||||
|
||||
The *vnoded* daemon is the program used to create a new namespace, and listen on a control channel for commands that may instantiate other processes. This daemon runs as PID 1 in the container. It is launched automatically by the CORE daemon. The control channel is a UNIX domain socket usually named */tmp/pycore.23098/n3*, for node 3 running on CORE session 23098, for example. Root privileges are required for creating a new namespace.
|
||||
The *vnoded* daemon is the program used to create a new namespace, and listen on a control channel for commands that
|
||||
may instantiate other processes. This daemon runs as PID 1 in the container. It is launched automatically by the CORE
|
||||
daemon. The control channel is a UNIX domain socket usually named */tmp/pycore.23098/n3*, for node 3 running on CORE
|
||||
session 23098, for example. Root privileges are required for creating a new namespace.
|
||||
|
||||
### vcmd command
|
||||
### vcmd
|
||||
|
||||
The *vcmd* program is used to connect to the *vnoded* daemon in a Linux network namespace, for running commands in the namespace. The CORE daemon uses the same channel for setting up a node and running processes within it. This program has two required arguments, the control channel name, and the command line to be run within the namespace. This command does not need to run with root privileges.
|
||||
The *vcmd* program is used to connect to the *vnoded* daemon in a Linux network namespace, for running commands in the
|
||||
namespace. The CORE daemon uses the same channel for setting up a node and running processes within it. This program
|
||||
has two required arguments, the control channel name, and the command line to be run within the namespace. This command
|
||||
does not need to run with root privileges.
|
||||
|
||||
When you double-click on a node in a running emulation, CORE will open a shell window for that node using a command such as:
|
||||
When you double-click on a node in a running emulation, CORE will open a shell window for that node using a command
|
||||
such as:
|
||||
|
||||
```shell
|
||||
gnome-terminal -e vcmd -c /tmp/pycore.50160/n1 -- bash
|
||||
|
@ -54,11 +125,14 @@ vcmd -c /tmp/pycore.50160/n1 -- /sbin/ip -4 ro
|
|||
|
||||
### core-cleanup script
|
||||
|
||||
A script named *core-cleanup* is provided to clean up any running CORE emulations. It will attempt to kill any remaining vnoded processes, kill any EMANE processes, remove the :file:`/tmp/pycore.*` session directories, and remove any bridges or *ebtables* rules. With a *-d* option, it will also kill any running CORE daemon.
|
||||
A script named *core-cleanup* is provided to clean up any running CORE emulations. It will attempt to kill any
|
||||
remaining vnoded processes, kill any EMANE processes, remove the :file:`/tmp/pycore.*` session directories, and remove
|
||||
any bridges or *ebtables* rules. With a *-d* option, it will also kill any running CORE daemon.
|
||||
|
||||
### netns command
|
||||
|
||||
The *netns* command is not used by CORE directly. This utility can be used to run a command in a new network namespace for testing purposes. It does not open a control channel for receiving further commands.
|
||||
The *netns* command is not used by CORE directly. This utility can be used to run a command in a new network namespace
|
||||
for testing purposes. It does not open a control channel for receiving further commands.
|
||||
|
||||
### Other Useful Commands
|
||||
|
||||
|
@ -110,4 +184,5 @@ brctl show
|
|||
vcmd -c /tmp/n1.ctl -- ping 10.0.0.2
|
||||
```
|
||||
|
||||
The above example script can be found as *twonodes.sh* in the *examples/netns* directory. Use *core-cleanup* to clean up after the script.
|
||||
The above example script can be found as *twonodes.sh* in the *examples/netns* directory. Use *core-cleanup* to clean
|
||||
up after the script.
|
||||
|
|
|
@ -66,17 +66,6 @@ sudo apt install python-configparser python-enum34 python-future python-grpcio p
|
|||
sudo apt install python3-configparser python3-enum34 python3-future python3-grpcio python3-lxml
|
||||
```
|
||||
|
||||
## Other Distros
|
||||
|
||||
The newly added gRPC API which depends on python library grpcio is not commonly found within system repos.
|
||||
To account for this it would be recommended to install the python dependencies using the **requirements.txt** found in
|
||||
the latest release.
|
||||
|
||||
```shell
|
||||
# will need to pip3 for python3 usage
|
||||
sudo pip install -r requirements.txt
|
||||
```
|
||||
|
||||
# Pre-Req Installing OSPF MDR
|
||||
|
||||
Virtual networks generally require some form of routing in order to work (e.g. to automatically populate routing
|
||||
|
|
Loading…
Reference in a new issue