Merge branch 'develop' into pydocupdates

This commit is contained in:
Huy Pham 2019-09-12 10:24:23 -07:00
commit 2bfcc9ef24
100 changed files with 5340 additions and 3488 deletions

View file

@ -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

View 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
View 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
View 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"
}
}
}

View file

@ -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

View file

@ -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):

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -3,6 +3,7 @@ Utilities for working with python struct data.
"""
import logging
from past.builtins import basestring

View file

@ -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)

View file

@ -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)]

View file

@ -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)

View file

@ -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):

View file

@ -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)

View file

@ -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
)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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])

View file

@ -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)

View file

@ -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)

View file

@ -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,
)

View file

@ -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

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()
)

View file

@ -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)

View file

@ -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"):

View file

@ -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)

View file

@ -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:

View file

@ -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

View file

@ -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):

View file

@ -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"

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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"])

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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."

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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")

View file

@ -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"

View file

@ -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))

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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 = ()

View file

@ -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__":

View file

@ -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)

View file

@ -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))

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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):

View file

@ -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

View file

@ -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,

View file

@ -1,12 +0,0 @@
import sys
import pytest
distributed = sys.argv[1]
pytest.main([
"-v",
"--distributed", distributed,
"--cov-report", "xml",
"--cov=.",
"tests"
])

View file

@ -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")

View file

@ -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)

View 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"

View file

@ -1,2 +0,0 @@
[pytest]
norecursedirs = distributed

View file

@ -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),
]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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):

View file

@ -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

View file

@ -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.

View file

@ -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