A Python library for Juju
=========================
-Front Matter
+NOTE: This is pre-release software. The implementation is usable but
+not complete.
+
+Please report bugs at https://github.com/juju/python-libjuju/issues.
+
+
+Requirements
------------
-NOTE: There is no implementation here yet. It is simply a mocked out
-design of what this library might look like. The design itself is not
-complete. Comments on the design, good or bad, are welcomed. Use cases
-are also appreciated as they will shape the design.
+* Python 3.5+
+* Juju 2.0+
+
+
+Design Notes
+------------
-The goal is to end up with a feature-full and officially supported
-python library for Juju.
+* Asynchronous - uses asyncio and async/await features of python 3.5
+* Websocket-level bindings are programmatically generated (indirectly) from the
+ Juju golang code, ensuring full api coverage
+* Provides an OO layer which encapsulates much of the websocket api and
+ provides familiar nouns and verbs (e.g. Model.deploy(), Application.add_unit(),
+ etc.)
-The focus right now is on Juju 2+ only.
-Design Ideas
+Installation
------------
-* Present an object-oriented interface to all the features of the Juju
- CLI
-* Do as much as possible through the API so that the library can be used
- without actually installing Juju
+ git clone https://github.com/juju/python-libjuju.git
+ cd python-libjuju
+ python setup.py install # make sure python is 3.5+
-Example Use Cases
------------------
-Please add more!
+Quickstart
+----------
+Here's a simple example that shows basic usage of the library. The example
+connects to the currently active Juju model, deploys a single unit of the
+ubuntu charm, then exits.
+
+More examples can be found in the `examples/` directory of the source tree,
+and in the documentation.
-Simple bootstrap/deploy
-+++++++++++++++++++++++
.. code:: python
- from juju import Juju
+ #!/usr/bin/python3.5
+
+ import asyncio
+ import logging
+
+ from juju.model import Model
+
+
+ async def run():
+ # Create a Model instance. We need to connect our Model to a Juju api
+ # server before we can use it.
+ model = Model()
+
+ # Connect to the currently active Juju model
+ await model.connect_current()
+
+ # Deploy a single unit of the ubuntu charm, using revision 0 from the
+ # stable channel of the Charm Store.
+ ubuntu_app = await model.deploy(
+ 'ubuntu-0',
+ service_name='ubuntu',
+ series='xenial',
+ channel='stable',
+ )
+
+ # Disconnect from the api server and cleanup.
+ model.disconnect()
+
+ # Stop the asyncio event loop.
+ model.loop.stop()
+
+
+ def main():
+ # Set logging level to debug so we can see verbose output from the
+ # juju library.
+ logging.basicConfig(level=logging.DEBUG)
+
+ # Quiet logging from the websocket library. If you want to see
+ # everything sent over the wire, set this to DEBUG.
+ ws_logger = logging.getLogger('websockets.protocol')
+ ws_logger.setLevel(logging.INFO)
+
+ # Create the asyncio event loop
+ loop = asyncio.get_event_loop()
+
+ # Queue up our `run()` coroutine for execution
+ loop.create_task(run())
- juju = Juju()
- lxd = juju.get_cloud('lxd')
- controller = lxd.bootstrap('lxd-test')
- model = controller.get_model('default')
+ # Start the event loop
+ loop.run_forever()
- # We might want an async and blocking version of deploy()?
- model.deploy('mediawiki-single')
- mediawiki = model.get_service('mediawiki')
- mediawiki.expose()
+ if __name__ == '__main__':
+ main()
Table of Contents
-----------------
.. toctree::
- :maxdepth: 2
+ :glob:
+ :maxdepth: 3
- api/modules
+ narrative/index
+ API Docs <api/modules>
--- /dev/null
+Connecting
+==========
+A Juju controller provides websocket endpoints for itself and each of its
+models. In order to do anything useful, the juju lib must connect to one of
+these endpoints. There are several ways to do this.
+
+
+To the Current Model
+--------------------
+Connect to the currently active Juju model (the one returned by
+`juju switch`). This only works if you have the Juju CLI client installed.
+
+.. code:: python
+
+ from juju.model import Model
+
+ model = Model()
+ await model.connect_current()
+
+
+To a Named Model
+----------------
+Connect to a model by name, using the same format as that returned from the
+`juju switch` command. The accepted format is '[controller:][user/]model'.
+This only works if you have the Juju CLI client installed.
+
+.. code:: python
+
+ # $ juju switch
+ # juju-2.0.1:admin/libjuju
+
+ from juju.model import Model
+
+ model = Model()
+ await model.connect_model('juju-2.0.1:admin/libjuju')
+
+
+To an API Endpoint with Username/Password Authentication
+--------------------------------------------------------
+The most flexible, but also most verbose, way to connect is using the API
+endpoint url and credentials directly. This method does NOT require the Juju
+CLI client to be installed.
+
+.. code:: python
+
+ from juju.model import Model
+
+ model = Model()
+
+ controller_endpoint = '10.0.4.171:17070'
+ model_uuid = 'e8399ac7-078c-4817-8e5e-32316d55b083'
+ username = 'admin'
+ password = 'f53f08cfc32a2e257fe5393271d89d62'
+
+ # Left out for brevity, but if you have a cert string you should pass it in.
+ # You can copy the cert from the output of The `juju show-controller`
+ # command.
+ cacert = None
+
+ await model.connect(
+ controller_endpoint,
+ model_uuid,
+ username,
+ password,
+ cacert,
+ )