Replace pass with NotImplementedError in method stubs
[osm/N2VC.git] / juju / unit.py
1 import logging
2
3 from dateutil.parser import parse as parse_date
4
5 from . import model
6 from .client import client
7
8 log = logging.getLogger(__name__)
9
10
11 class Unit(model.ModelEntity):
12 @property
13 def agent_status(self):
14 """Returns the current agent status string.
15
16 """
17 return self.safe_data['agent-status']['current']
18
19 @property
20 def agent_status_since(self):
21 """Get the time when the `agent_status` was last updated.
22
23 """
24 return parse_date(self.safe_data['agent-status']['since'])
25
26 @property
27 def agent_status_message(self):
28 """Get the agent status message.
29
30 """
31 return self.safe_data['agent-status']['message']
32
33 @property
34 def workload_status(self):
35 """Returns the current workload status string.
36
37 """
38 return self.safe_data['workload-status']['current']
39
40 @property
41 def workload_status_since(self):
42 """Get the time when the `workload_status` was last updated.
43
44 """
45 return parse_date(self.safe_data['workload-status']['since'])
46
47 @property
48 def workload_status_message(self):
49 """Get the workload status message.
50
51 """
52 return self.safe_data['workload-status']['message']
53
54 @property
55 def tag(self):
56 return 'unit-%s' % self.name.replace('/', '-')
57
58 def add_storage(self, name, constraints=None):
59 """Add unit storage dynamically.
60
61 :param str name: Storage name, as specified by the charm
62 :param str constraints: Comma-separated list of constraints in the
63 form 'POOL,COUNT,SIZE'
64
65 """
66 raise NotImplementedError()
67
68 def collect_metrics(self):
69 """Collect metrics on this unit.
70
71 """
72 raise NotImplementedError()
73
74 async def destroy(self):
75 """Destroy this unit.
76
77 """
78 app_facade = client.ApplicationFacade()
79 app_facade.connect(self.connection)
80
81 log.debug(
82 'Destroying %s', self.name)
83
84 return await app_facade.DestroyUnits([self.name])
85 remove = destroy
86
87 def get_resources(self, details=False):
88 """Return resources for this unit.
89
90 :param bool details: Include detailed info about resources used by each
91 unit
92
93 """
94 raise NotImplementedError()
95
96 def resolved(self, retry=False):
97 """Mark unit errors resolved.
98
99 :param bool retry: Re-execute failed hooks
100
101 """
102 raise NotImplementedError()
103
104 async def run(self, command, timeout=None):
105 """Run command on this unit.
106
107 :param str command: The command to run
108 :param int timeout: Time to wait before command is considered failed
109 :returns: A :class:`juju.action.Action` instance.
110
111 """
112 action = client.ActionFacade()
113 action.connect(self.connection)
114
115 log.debug(
116 'Running `%s` on %s', command, self.name)
117
118 res = await action.Run(
119 [],
120 command,
121 [],
122 timeout,
123 [self.name],
124 )
125 return await self.model.wait_for_action(res.results[0].action.tag)
126
127 async def run_action(self, action_name, **params):
128 """Run an action on this unit.
129
130 :param str action_name: Name of action to run
131 :param \*\*params: Action parameters
132 :returns: A :class:`juju.action.Action` instance.
133
134 Note that this only enqueues the action. You will need to call
135 ``action.wait()`` on the resulting `Action` instance if you wish
136 to block until the action is complete.
137
138 """
139 action_facade = client.ActionFacade()
140 action_facade.connect(self.connection)
141
142 log.debug('Starting action `%s` on %s', action_name, self.name)
143
144 res = await action_facade.Enqueue([client.Action(
145 name=action_name,
146 parameters=params,
147 receiver=self.tag,
148 )])
149 action = res.results[0].action
150 error = res.results[0].error
151 if error and error.code == 'not found':
152 raise ValueError('Action `%s` not found on %s' % (action_name,
153 self.name))
154 elif error:
155 raise Exception('Unknown action error: %s' % error.serialize())
156 action_id = action.tag[len('action-'):]
157 log.debug('Action started as %s', action_id)
158 # we mustn't use wait_for_action because that blocks until the
159 # action is complete, rather than just being in the model
160 return await self.model._wait_for_new('action', action_id)
161
162 def scp(
163 self, source_path, user=None, destination_path=None, proxy=False,
164 scp_opts=None):
165 """Transfer files to this unit.
166
167 :param str source_path: Path of file(s) to transfer
168 :param str user: Remote username
169 :param str destination_path: Destination of transferred files on
170 remote machine
171 :param bool proxy: Proxy through the Juju API server
172 :param str scp_opts: Additional options to the `scp` command
173
174 """
175 raise NotImplementedError()
176
177 def set_meter_status(self):
178 """Set the meter status on this unit.
179
180 """
181 raise NotImplementedError()
182
183 def ssh(
184 self, command, user=None, proxy=False, ssh_opts=None):
185 """Execute a command over SSH on this unit.
186
187 :param str command: Command to execute
188 :param str user: Remote username
189 :param bool proxy: Proxy through the Juju API server
190 :param str ssh_opts: Additional options to the `ssh` command
191
192 """
193 raise NotImplementedError()
194
195 def status_history(self, num=20, utc=False):
196 """Get status history for this unit.
197
198 :param int num: Size of history backlog
199 :param bool utc: Display time as UTC in RFC3339 format
200
201 """
202 raise NotImplementedError()
203
204 async def is_leader_from_status(self):
205 """
206 Check to see if this unit is the leader. Returns True if so, and
207 False if it is not, or if leadership does not make sense
208 (e.g., there is no leader in this application.)
209
210 This method is a kluge that calls FullStatus in the
211 ClientFacade to get its information. Once
212 https://bugs.launchpad.net/juju/+bug/1643691 is resolved, we
213 should add a simple .is_leader property, and deprecate this
214 method.
215
216 """
217 app = self.name.split("/")[0]
218
219 c = client.ClientFacade()
220 c.connect(self.model.connection)
221
222 status = await c.FullStatus(None)
223
224 # FullStatus may be more up to date than our model, and the
225 # unit may have gone away, or we may be doing something silly,
226 # like trying to fetch leadership for a subordinate, which
227 # will not be filed where we expect in the model. In those
228 # cases, we may simply return False, as a nonexistent or
229 # subordinate unit is not a leader.
230 if not status.applications.get(app):
231 return False
232
233 if not status.applications[app].get('units'):
234 return False
235
236 if not status.applications[app]['units'].get(self.name):
237 return False
238
239 return status.applications[app]['units'][self.name].get('leader',
240 False)
241
242 async def get_metrics(self):
243 """Get metrics for the unit.
244
245 :return: Dictionary of metrics for this unit.
246
247 """
248 metrics = await self.model.get_metrics(self.tag)
249 return metrics[self.name]