{ "info": { "author": "Frank Cangialosi", "author_email": "frankc@csail.mit.edu", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Programming Language :: Rust", "Topic :: System :: Networking" ], "description": "# pyportus\n\nThis module provides a python interface to the Portus CCP implementation.\n\n\n## Setup\n\nTo build and use the python bindings, you also need setuptools_rust\n\n```bash\nsudo pip install setuptools_rust\n```\n\nYou need to tell it to use the nightly version of rust since some features\nare still experimental:\n* Check where the package was installed: `pip show setuptools_rust`\n* Edit `packages/setuptools_rust/build.py`\n* Search for the line containing \"rustc\" (should be ~102), and change the args to be `[\"cargo\", \"+nightly\", \"rustc\", ...`\n\nNow, rather than running make, you can build with\n\n```bash\npython setup.py develop\n```\n\nDepending on your python environment setup, you may need to run this with `sudo`\n(and ensure that your `PATH` variable is preserved):\n\n```bash\nsudo env PATH=$PATH python setup.py develop\n```\n\nNow you should be able to import the package like so:\n\n```python\nimport portus\n```\n\n\n\n## Writing Algorithms\n\n\n### Overview\n\nAn algorithm in portus is represented by a Python class and an instance of this class represents a single TCP flow. A new instance is created *for each* flow. \n\nThis class must be a subclass of `portus.AlgBase` and must implement the two \nfollowing method signatures:\n* `on_create(self)` \n* `on_report(self, r)` \n - `r` is a Report object containing all the fields defined in your datapath program, as well as the current `Cwnd` and `Rate`. Suppose your program defines just a single variable: `(def (acked 0))`, where `acked` adds up the total bytes acked since the last report. This value can be accessed as `r.acked`. Similarly, you can access the cwnd or rate as `r.Cwnd` and `r.Rate` (captialization important!).\n\nEach instantiation of the class will automatically have two fields inside self:\n - `self.datapath` is a pointer to the datapath object that can be used to install\n new datapath programs. It has two available methods:\n 1. `datapath.install( str )`, which takes a datapath program as a string. It compiles the program and installs it in the datapath. It does not return anything, though it may raise an exception if your program fails to compile. \n 2. `datapath.update_field(field, val)`, which takes a variable in the `Report` scope of your datapath program and sets the value to `val`. For example, to update just the cwnd, you could use `datapath.update_field(\"Cwnd\", 10000)` (note: cwnd is denoted in bytes, not packets). \n - `self.datapath_info` is a struct containing fields about this particular flow from the datapath (this could be used, for example, in `on_create` to set an initial cwnd based on the datapath's `mss`)\n * `sock_id`: unique id of this flow in the datapath\n * `init_cwnd`: the initial congestion window this flow will have until you set it\n * `src_ip`, `src_port`, `dst_ip`, `dst_port`: the ip address and port of the source and destination for the flow \n\n\n### Datapath Programs\n\nDatapath programs are used to (1) define *which* statistics to send back to your usespace program and *how often* and (2) set the congestion window and/or pacing rate. A datapath program is written in a very simple lisp-like dialect and consists of a single variable definition line followed by any number of when clauses:\n```\n(def ( ... ) ( ... ))\n(when (event) (\n do_stuff ...\n)\n(when (other_event) (\n do_other_stuff ...\n)\n```\n\n# NOTE: the following info is out of date as the datapath program API has been\nupdated\n\n##### 1. Report Variable Definitions\n\nExample: `(def (Report.acked 0) (Report.rtt 0) (Report.timeout false))`\n\nThis line defines the names and initial values of variables in the __report scope__. Calling `(report)` in your datapath program results in a call to your algorithm's `on_report` function with the current value of these variables. *After the call these variables are reset back to their initial value.*\n\n__NOTE__: Variables in datapath programs are written as `{scope}.{name}`. For example, the `acked` variable in the `Report` scope is written as `Report.acked`. Therefore, *all variables defined in this line must* start with `Report.` However, when you access them in `on_report`, you just provide the variable name. In our example above, `Report.rtt` defines the variable `rtt` in the `Report` scope. If we want to access this value in `on_report(r)`, we'd use `r.rtt` (i.e. *not* `r.Report.rtt`). \n\n\n##### 2. When Clauses\n\nWhen clauses consist of a boolean expression and a set of instructions. On each ack, the datapath checks the boolean expression, and if it evaluates to `true`, it runs the set of instructions. For example, the following when clause sends a report (i.e. calls the `on_report` function) once every rtt:\n```\n(when (> Micros Flow.rtt_sample_us)\n (report)\n)\n```\n\n### Putting it all together\n\nA sample algorithm definition showing the full API:\n```python\nimport portus\n\n# Class must sublcass portus.AlgBase\nclass SampleCCAlg(portus.AlgBase):\n # Init must take exactly these parameters\n def __init__(self, datapath, datapath_info):\n # Store a copy of the datapath and info for later\n self.datapath = datapath\n self.datapath_info = datapath_info\n\n # Internally store an initial cwnd value\n self.cwnd = 10 * self.datapath_info.mss\n\n # Install an initial datapath program to keep track of the RTT and report it once per RTT\n # The first when clause is true on every single ack,\n # which means the 'Report.rtt' field will always keep the latest rtt sample\n # The second when clause is true once one rtt's worth of time has passed, \n # at which point it will trigger on_report, and Micros (and Report.rtt) will be reset to 0\n self.datapath.install(\"\"\"\\\n (def\n (Report.rtt 0)\n )\n (when true\n (:= Report.rtt Flow.rtt_sample_us)\n (fallthrough)\n )\n (when (> Micros Flow.rtt_sample_us)\n (report)\n )\n \"\"\")\n\n # This function will be called once per RTT, and the report struct `r` will contain:\n # \"rtt\", \"Cwnd\", and \"Rate\"\n def on_report(self, r):\n # Compute new cwnd internally \n # If the rtt has decreased, increase the cwnd by 1 packet, else decrease by 1 packet\n if self.last_rtt < r.rtt:\n self.cwnd += self.datapath_info.mss\n else:\n self.cwnd -= self.datapath_info.mss\n self.last_rtt = r.rtt\n\n # Send this new value of cwnd to the datapath\n self.datapath.update_field(\"Cwnd\", self.cwnd)\n\n```\n\n\n### Important Notes\n1. You should install an initial datapath program in your `__init__` implementation, otherwise you will not receive any reports and nothing else will happen. You can always install a different datapath program later when handling `on_report`.\n2. If you want to print anything, you should use `sys.stderr.write()` (note that you need to `import sys` and that it doesn't automatically add new lines for you like `print` does). \n3. You *must* store a reference to `datapath` in `self` called \"datapath\" (i.e. `self.datapath = datapath`), because the library internally uses this to access the datapath struct as well. \n\n\n### Starting CCP \n\nThe CCP entry point is `portus.connect(ipc_type, class, debug, blocking)`:\n* `ipc_type (string)`: (netlink | unix | char) on linux or (unix) on mac\n* `class`: your algorithm class, e.g. `SampleCCAlg`\n* `debug (bool)`: if true, the CCP will log all messages passed between the ccp and datapath\n* `blocking (bool)`: if true, use blocking ipc reads, otherwise use non-blocking\n\nFor example: `portus.connect(\"netlink\", SampleCCAlg, debug=True, blocking=True)`. \n\nRegardless of whether you use blocking or non-blocking sockets, `connect` will block forever (to stop the CCP just send ctrl+c or kill the process). \n\n### Example\n\nFor a full working example of both defining an algorithm and running the CCP, see the simple AIMD scheme in `./aimd.py` and try running it: `sudo python aimd.py`. \n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/ccp-project/portus", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "portus", "package_url": "https://pypi.org/project/portus/", "platform": "", "project_url": "https://pypi.org/project/portus/", "project_urls": { "Homepage": "https://github.com/ccp-project/portus" }, "release_url": "https://pypi.org/project/portus/0.5.1/", "requires_dist": [ "pyportus" ], "requires_python": "", "summary": "Python bindings for the Portus implementation of CCP", "version": "0.5.1" }, "last_serial": 5184133, "releases": { "0.5.1": [ { "comment_text": "", "digests": { "md5": "da165022bd02dadbbc08e7614d9a8e92", "sha256": "c359ba2f9602f4f56596dadd0dc29afce69a8d5d0cc1afef235ed34093c7f0b6" }, "downloads": -1, "filename": "portus-0.5.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "da165022bd02dadbbc08e7614d9a8e92", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 7467, "upload_time": "2019-04-24T19:40:47", "url": "https://files.pythonhosted.org/packages/98/64/ba22ca66cfaa3f61560d4a83b490f46727eea989f4eb1ea4f8bd1b4119e6/portus-0.5.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "640f7e94e3a89741cf7be434440a522e", "sha256": "fb1449a0a1dec2603f9fd5a8b0dfa00136da8be5a11c59b9107b5b4be9fd0269" }, "downloads": -1, "filename": "portus-0.5.1.tar.gz", "has_sig": false, "md5_digest": "640f7e94e3a89741cf7be434440a522e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7323, "upload_time": "2019-04-24T19:40:53", "url": "https://files.pythonhosted.org/packages/28/5c/9ffb33399cfe39fb9973db3013fc6cf2b734e0464bdc51e2fd7af725db05/portus-0.5.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "da165022bd02dadbbc08e7614d9a8e92", "sha256": "c359ba2f9602f4f56596dadd0dc29afce69a8d5d0cc1afef235ed34093c7f0b6" }, "downloads": -1, "filename": "portus-0.5.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "da165022bd02dadbbc08e7614d9a8e92", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 7467, "upload_time": "2019-04-24T19:40:47", "url": "https://files.pythonhosted.org/packages/98/64/ba22ca66cfaa3f61560d4a83b490f46727eea989f4eb1ea4f8bd1b4119e6/portus-0.5.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "640f7e94e3a89741cf7be434440a522e", "sha256": "fb1449a0a1dec2603f9fd5a8b0dfa00136da8be5a11c59b9107b5b4be9fd0269" }, "downloads": -1, "filename": "portus-0.5.1.tar.gz", "has_sig": false, "md5_digest": "640f7e94e3a89741cf7be434440a522e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7323, "upload_time": "2019-04-24T19:40:53", "url": "https://files.pythonhosted.org/packages/28/5c/9ffb33399cfe39fb9973db3013fc6cf2b734e0464bdc51e2fd7af725db05/portus-0.5.1.tar.gz" } ] }