3 A Juju controller provides websocket endpoints for each of its
4 models. In order to do anything useful with a model, the juju lib must
5 connect to one of these endpoints. There are several ways to do this.
7 For api docs, see :class:`juju.model.Model`.
10 Connecting to the Current Model
11 -------------------------------
12 Connect to the currently active Juju model (the one returned by
13 `juju switch`). This only works if you have the Juju CLI client installed.
17 from juju.model import Model
20 await model.connect_current()
23 Connecting to a Named Model
24 ---------------------------
25 Connect to a model by name, using the same format as that returned from the
26 `juju switch` command. The accepted format is '[controller:][user/]model'.
27 This only works if you have the Juju CLI client installed.
32 # juju-2.0.1:admin/libjuju
34 from juju.model import Model
37 await model.connect_model('juju-2.0.1:admin/libjuju')
40 Connecting with Username/Password Authentication
41 ------------------------------------------------
42 The most flexible, but also most verbose, way to connect is using the API
43 endpoint url and credentials directly. This method does NOT require the Juju
44 CLI client to be installed.
48 from juju.model import Model
52 controller_endpoint = '10.0.4.171:17070'
53 model_uuid = 'e8399ac7-078c-4817-8e5e-32316d55b083'
55 password = 'f53f08cfc32a2e257fe5393271d89d62'
57 # Left out for brevity, but if you have a cert string you should pass it in.
58 # You can copy the cert from the output of The `juju show-controller`
71 Connecting with Macaroon Authentication
72 ---------------------------------------
73 To connect to a shared model, or a model an a shared controller, you'll need
74 to use macaroon authentication. The simplest example is shown below, and uses
75 already-discharged macaroons from the local filesystem. This will work if you
76 have the Juju CLI installed.
80 The library does not yet contain support for fetching and discharging
81 macaroons. Until it does, if you want to use macaroon auth, you'll need
82 to supply already-discharged macaroons yourself.
86 from juju.client.connection import get_macaroons()
87 from juju.model import Model
91 controller_endpoint = '10.0.4.171:17070'
92 model_uuid = 'e8399ac7-078c-4817-8e5e-32316d55b083'
96 macaroons = get_macaroons()
108 Creating and Destroying a Model
109 -------------------------------
110 Example of creating a new model and then destroying it. See
111 :meth:`juju.controller.Controller.add_model` and
112 :meth:`juju.controller.Controller.destroy_model` for more info.
116 from juju.controller import Controller
118 controller = Controller()
119 await controller.connect_current()
121 # Create our new model
122 model = await controller.add_model(
123 'mymodel', # name of your new model
126 # Do stuff with our model...
129 await model.disconnect()
130 await controller.destroy_model(model.info.uuid)
134 Reacting to Changes in a Model
135 ------------------------------
136 To watch for and respond to changes in a model, register an observer with the
137 model. The easiest way to do this is by creating a
138 :class:`juju.model.ModelObserver` subclass.
142 from juju.model import Model, ModelObserver
144 class MyModelObserver(ModelObserver):
145 async def on_change(self, delta, old, new, model):
146 # The raw change data (dict) from the websocket.
149 # The entity type (str) affected by this change.
150 # One of ('action', 'application', 'annotation', 'machine',
151 # 'unit', 'relation')
154 # The type (str) of change.
155 # One of ('add', 'change', 'remove')
158 # The 'old' and 'new' parameters are juju.model.ModelEntity
159 # instances which represent an entity in the model both before
160 # this change was applied (old) and after (new).
162 # If an entity is being added to the model, the 'old' param
164 if delta.type == 'add':
167 # If an entity is being removed from the model, the 'new' param
169 if delta.type == 'remove':
172 # The 'old' and 'new' parameters, when not None, will be instances
173 # of a juju.model.ModelEntity subclass. The type of the subclass
174 # depends on the value of 'delta.entity', for example:
178 # 'action' -> juju.action.Action
179 # 'application' -> juju.application.Application
180 # 'annotation' -> juju.annotation.Annotation
181 # 'machine' -> juju.machine.Machine
182 # 'unit' -> juju.unit.Unit
183 # 'relation' -> juju.relation.Relation
185 # Finally, the 'model' parameter is a reference to the
186 # juju.model.Model instance to which this observer is attached.
191 await model.connect_current()
193 model.add_observer(MyModelObserver())
196 Every change in the model will result in a call to the `on_change()`
197 method of your observer(s).
199 To target your code more precisely, define method names that correspond
200 to the entity and type of change that you wish to handle.
204 from juju.model import Model, ModelObserver
206 class MyModelObserver(ModelObserver):
207 async def on_application_change(self, delta, old, new, model):
208 # Both 'old' and 'new' params will be instances of
209 # juju.application.Application
212 async def on_unit_remove(self, delta, old, new, model):
213 # Since a unit is being removed, the 'new' param will always
214 # be None in this handler. The 'old' param will be an instance
215 # of juju.unit.Unit - the state of the unit before it was removed.
218 async def on_machine_add(self, delta, old, new, model):
219 # Since a machine is being added, the 'old' param will always be
220 # None in this handler. The 'new' param will be an instance of
221 # juju.machine.Machine.
224 async def on_change(self, delta, old, new, model):
225 # The catch-all handler - will be called whenever a more
226 # specific handler method is not defined.
229 Any :class:`juju.model.ModelEntity` object can be observed directly by
230 registering callbacks on the object itself.
236 async def on_app_change(delta, old, new, model):
237 logging.debug('App changed: %r', new)
239 async def on_app_remove(delta, old, new, model):
240 logging.debug('App removed: %r', old)
242 ubuntu_app = await model.deploy(
244 application_name='ubuntu',
248 ubuntu_app.on_change(on_app_change)
249 ubuntu_app.on_remove(on_app_remove)