Docs
[osm/N2VC.git] / docs / narrative / model.rst
1 Models
2 ======
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.
6
7 For api docs, see :class:`juju.model.Model`.
8
9
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.
14
15 .. code:: python
16
17   from juju.model import Model
18
19   model = Model()
20   await model.connect_current()
21
22
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.
28
29 .. code:: python
30
31   # $ juju switch
32   # juju-2.0.1:admin/libjuju
33
34   from juju.model import Model
35
36   model = Model()
37   await model.connect_model('juju-2.0.1:admin/libjuju')
38
39
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.
45
46 .. code:: python
47
48   from juju.model import Model
49
50   model = Model()
51
52   controller_endpoint = '10.0.4.171:17070'
53   model_uuid = 'e8399ac7-078c-4817-8e5e-32316d55b083'
54   username = 'admin'
55   password = 'f53f08cfc32a2e257fe5393271d89d62'
56
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`
59   # command.
60   cacert = None
61
62   await model.connect(
63       controller_endpoint,
64       model_uuid,
65       username,
66       password,
67       cacert,
68   )
69
70
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.
77
78 .. note::
79
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.
83
84 .. code:: python
85
86   from juju.client.connection import get_macaroons()
87   from juju.model import Model
88
89   model = Model()
90
91   controller_endpoint = '10.0.4.171:17070'
92   model_uuid = 'e8399ac7-078c-4817-8e5e-32316d55b083'
93   username = None
94   password = None
95   cacert = None
96   macaroons = get_macaroons()
97
98   await model.connect(
99       controller_endpoint,
100       model_uuid,
101       username,
102       password,
103       cacert,
104       macaroons,
105   )
106
107
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.
113
114 .. code:: python
115
116   from juju.controller import Controller
117
118   controller = Controller()
119   await controller.connect_current()
120
121   # Create our new model
122   model = await controller.add_model(
123       'mymodel',  # name of your new model
124   )
125
126   # Do stuff with our model...
127
128   # Destroy the model
129   await model.disconnect()
130   await controller.destroy_model(model.info.uuid)
131   model = None
132
133
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.
139
140 .. code:: python
141
142   from juju.model import Model, ModelObserver
143
144   class MyModelObserver(ModelObserver):
145       async def on_change(self, delta, old, new, model):
146           # The raw change data (dict) from the websocket.
147           print(delta.data)
148
149           # The entity type (str) affected by this change.
150           # One of ('action', 'application', 'annotation', 'machine',
151           # 'unit', 'relation')
152           print(delta.entity)
153
154           # The type (str) of change.
155           # One of ('add', 'change', 'remove')
156           print(delta.type)
157
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).
161
162           # If an entity is being added to the model, the 'old' param
163           # will be None.
164           if delta.type == 'add':
165               assert(old is None)
166
167           # If an entity is being removed from the model, the 'new' param
168           # will be None.
169           if delta.type == 'remove':
170               assert(new is None)
171
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:
175           #
176           # delta.entity     type
177           # ------------     ----
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
184
185           # Finally, the 'model' parameter is a reference to the
186           # juju.model.Model instance to which this observer is attached.
187           print(id(model))
188
189
190   model = Model()
191   await model.connect_current()
192
193   model.add_observer(MyModelObserver())
194
195
196 Every change in the model will result in a call to the `on_change()`
197 method of your observer(s).
198
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.
201
202 .. code:: python
203
204   from juju.model import Model, ModelObserver
205
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
210           pass
211
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.
216           pass
217
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.
222           pass
223
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.
227
228
229 Any :class:`juju.model.ModelEntity` object can be observed directly by
230 registering callbacks on the object itself.
231
232 .. code:: python
233
234   import logging
235
236   async def on_app_change(delta, old, new, model):
237       logging.debug('App changed: %r', new)
238
239   async def on_app_remove(delta, old, new, model):
240       logging.debug('App removed: %r', old)
241
242   ubuntu_app = await model.deploy(
243       'ubuntu',
244       application_name='ubuntu',
245       series='trusty',
246       channel='stable',
247   )
248   ubuntu_app.on_change(on_app_change)
249   ubuntu_app.on_remove(on_app_remove)