Implement Unit.run_action
[osm/N2VC.git] / juju / unit.py
1 import logging
2 from datetime import datetime
3
4 from . import model
5 from .client import client
6
7 log = logging.getLogger(__name__)
8
9
10 class Unit(model.ModelEntity):
11 @property
12 def agent_status(self):
13 """Returns the current agent status string.
14
15 """
16 return self.data['agent-status']['current']
17
18 @property
19 def agent_status_since(self):
20 """Get the time when the `agent_status` was last updated.
21
22 """
23 since = self.data['agent-status']['since']
24 # Juju gives us nanoseconds, but Python only supports microseconds
25 since = since[:26]
26 return datetime.strptime(since, "%Y-%m-%dT%H:%M:%S.%f")
27
28 @property
29 def agent_status_message(self):
30 """Get the agent status message.
31
32 """
33 return self.data['agent-status']['message']
34
35 @property
36 def workload_status(self):
37 """Returns the current workload status string.
38
39 """
40 return self.data['workload-status']['current']
41
42 @property
43 def workload_status_since(self):
44 """Get the time when the `workload_status` was last updated.
45
46 """
47 since = self.data['workload-status']['since']
48 # Juju gives us nanoseconds, but Python only supports microseconds
49 since = since[:26]
50 return datetime.strptime(since, "%Y-%m-%dT%H:%M:%S.%f")
51
52 @property
53 def workload_status_message(self):
54 """Get the workload status message.
55
56 """
57 return self.data['workload-status']['message']
58
59 @property
60 def tag(self):
61 return 'unit-%s' % self.name.replace('/', '-')
62
63 def add_storage(self, name, constraints=None):
64 """Add unit storage dynamically.
65
66 :param str name: Storage name, as specified by the charm
67 :param str constraints: Comma-separated list of constraints in the
68 form 'POOL,COUNT,SIZE'
69
70 """
71 pass
72
73 def collect_metrics(self):
74 """Collect metrics on this unit.
75
76 """
77 pass
78
79 async def destroy(self):
80 """Destroy this unit.
81
82 """
83 app_facade = client.ApplicationFacade()
84 app_facade.connect(self.connection)
85
86 log.debug(
87 'Destroying %s', self.name)
88
89 return await app_facade.DestroyUnits([self.name])
90 remove = destroy
91
92 def get_resources(self, details=False):
93 """Return resources for this unit.
94
95 :param bool details: Include detailed info about resources used by each
96 unit
97
98 """
99 pass
100
101 def resolved(self, retry=False):
102 """Mark unit errors resolved.
103
104 :param bool retry: Re-execute failed hooks
105
106 """
107 pass
108
109 async def run(self, command, timeout=None):
110 """Run command on this unit.
111
112 :param str command: The command to run
113 :param int timeout: Time to wait before command is considered failed
114
115 Returns a tuple containing the stdout, stderr, and return code
116 from the command.
117
118 """
119 action = client.ActionFacade()
120 action.connect(self.connection)
121
122 log.debug(
123 'Running `%s` on %s', command, self.name)
124
125 res = await action.Run(
126 [],
127 command,
128 [],
129 timeout,
130 [self.name],
131 )
132 return await self.model.wait_for_action(res.results[0].action.tag)
133
134 async def run_action(self, action_name, **params):
135 """Run an action on this unit.
136
137 :param str action_name: Name of action to run
138 :param \*\*params: Action parameters
139 :returns: An `juju.action.Action` instance.
140
141 Note that this only enqueues the action. You will need to call
142 ``action.wait()`` on the resulting `Action` instance if you wish
143 to block until the action is complete.
144 """
145 action_facade = client.ActionFacade()
146 action_facade.connect(self.connection)
147
148 log.debug('Starting action `%s` on %s', action_name, self.name)
149
150 res = await action_facade.Enqueue([client.Action(
151 name=action_name,
152 parameters=params,
153 receiver=self.tag,
154 )])
155 action = res.results[0].action
156 error = res.results[0].error
157 if error and error.code == 'not found':
158 raise ValueError('Action `%s` not found on %s' % (action_name,
159 self.name))
160 elif error:
161 raise Exception('Unknown action error: %s' % error.serialize())
162 action_id = action.tag[len('action-'):]
163 log.debug('Action started as %s', action_id)
164 # we can't use wait_for_new here because we don't
165 # consistently (ever?) get an "add" delta for the action
166 return await self.model._wait('action', action_id, None)
167
168 def scp(
169 self, source_path, user=None, destination_path=None, proxy=False,
170 scp_opts=None):
171 """Transfer files to this unit.
172
173 :param str source_path: Path of file(s) to transfer
174 :param str user: Remote username
175 :param str destination_path: Destination of transferred files on
176 remote machine
177 :param bool proxy: Proxy through the Juju API server
178 :param str scp_opts: Additional options to the `scp` command
179
180 """
181 pass
182
183 def set_meter_status(self):
184 """Set the meter status on this unit.
185
186 """
187 pass
188
189 def ssh(
190 self, command, user=None, proxy=False, ssh_opts=None):
191 """Execute a command over SSH on this unit.
192
193 :param str command: Command to execute
194 :param str user: Remote username
195 :param bool proxy: Proxy through the Juju API server
196 :param str ssh_opts: Additional options to the `ssh` command
197
198 """
199 pass
200
201 def status_history(self, num=20, utc=False):
202 """Get status history for this unit.
203
204 :param int num: Size of history backlog
205 :param bool utc: Display time as UTC in RFC3339 format
206
207 """
208 pass