161
by Curtis Hovey
Windows and py3 compatability. |
1 |
from __future__ import print_function |
2 |
||
1306.1.1
by Curtis Hovey
Save spike to get leader and followers. |
3 |
from collections import ( |
4 |
defaultdict, |
|
5 |
namedtuple, |
|
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
6 |
)
|
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
7 |
from contextlib import ( |
8 |
contextmanager, |
|
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
9 |
)
|
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
10 |
from copy import deepcopy |
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
11 |
from datetime import ( |
12 |
datetime, |
|
13 |
timedelta, |
|
14 |
)
|
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
15 |
import errno |
751.1.1
by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt |
16 |
from itertools import chain |
1437.1.1
by Aaron Bentley
Speed up tests by using JSON for status. |
17 |
import json |
715.2.1
by John George
add debug logging option to show command and output from juju calls made from get_juju_output |
18 |
import logging |
161
by Curtis Hovey
Windows and py3 compatability. |
19 |
import os |
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
20 |
import re |
1695.2.12
by Andrew Beach
Fixes for more comments. |
21 |
import shutil |
2
by Aaron Bentley
Added initial deploy_stack. |
22 |
import subprocess |
23 |
import sys |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
24 |
from tempfile import NamedTemporaryFile |
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
25 |
import time |
2
by Aaron Bentley
Added initial deploy_stack. |
26 |
|
1703.1.8
by Andrew Beach
Added test labels. |
27 |
import pexpect |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
28 |
import yaml |
29 |
||
30 |
from jujuconfig import ( |
|
31 |
get_environments_path, |
|
32 |
get_jenv_path, |
|
777.2.1
by Aaron Bentley
Always delete jenv in industrial test. |
33 |
get_juju_home, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
34 |
get_selected_environment, |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
35 |
)
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
36 |
from utility import ( |
37 |
check_free_disk_space, |
|
777.2.1
by Aaron Bentley
Always delete jenv in industrial test. |
38 |
ensure_deleted, |
1091.4.1
by James Tunnicliffe
Merged upstream |
39 |
ensure_dir, |
1167.2.1
by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use |
40 |
is_ipv6_address, |
1417.1.1
by Seman
Added more resource CI tests. |
41 |
JujuResourceTimeout, |
763.1.2
by Curtis Hovey
Added puase to sleep after ha, but remove it for tests. |
42 |
pause, |
1401.2.12
by Christopher Lee
Safer command quoting for expect(). |
43 |
quote, |
1477.3.1
by Andrew Wilkins
beebop |
44 |
qualified_model_name, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
45 |
scoped_environ, |
1703.1.6
by Andrew Beach
Renamed the function to skip_on_missing_file. |
46 |
skip_on_missing_file, |
1310.1.2
by Curtis Hovey
Address ipv6 split address and port. |
47 |
split_address_port, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
48 |
temp_dir, |
1477.3.1
by Andrew Wilkins
beebop |
49 |
unqualified_model_name, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
50 |
until_timeout, |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
51 |
)
|
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
52 |
|
2
by Aaron Bentley
Added initial deploy_stack. |
53 |
|
1092.2.2
by Aaron Bentley
Fix lint. |
54 |
__metaclass__ = type |
55 |
||
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
56 |
AGENTS_READY = set(['started', 'idle']) |
163
by Curtis Hovey
Extracted the windows command incase it needs to be reused. |
57 |
WIN_JUJU_CMD = os.path.join('\\', 'Progra~2', 'Juju', 'juju.exe') |
58 |
||
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
59 |
JUJU_DEV_FEATURE_FLAGS = 'JUJU_DEV_FEATURE_FLAGS' |
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
60 |
CONTROLLER = 'controller' |
61 |
KILL_CONTROLLER = 'kill-controller' |
|
62 |
SYSTEM = 'system' |
|
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
63 |
|
1341.2.1
by Aaron Bentley
Move container support knowledge to jujupy. |
64 |
KVM_MACHINE = 'kvm' |
65 |
LXC_MACHINE = 'lxc' |
|
66 |
LXD_MACHINE = 'lxd' |
|
67 |
||
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
68 |
_DEFAULT_BUNDLE_TIMEOUT = 3600 |
69 |
||
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
70 |
_jes_cmds = {KILL_CONTROLLER: { |
1199
by Curtis Hovey
Revert lp:~sinzui/juju-ci-tools/kill-controller-command r1198 because it exposes that controllers are |
71 |
'create': 'create-environment', |
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
72 |
'kill': KILL_CONTROLLER, |
1306.1.2
by Curtis Hovey
Fix missing brace. |
73 |
}}
|
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
74 |
for super_cmd in [SYSTEM, CONTROLLER]: |
1199
by Curtis Hovey
Revert lp:~sinzui/juju-ci-tools/kill-controller-command r1198 because it exposes that controllers are |
75 |
_jes_cmds[super_cmd] = { |
76 |
'create': '{} create-environment'.format(super_cmd), |
|
77 |
'kill': '{} kill'.format(super_cmd), |
|
1306.1.1
by Curtis Hovey
Save spike to get leader and followers. |
78 |
}
|
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
79 |
|
1091.4.1
by James Tunnicliffe
Merged upstream |
80 |
log = logging.getLogger("jujupy") |
81 |
||
163
by Curtis Hovey
Extracted the windows command incase it needs to be reused. |
82 |
|
1504.1.1
by Aaron Bentley
Use specific class for config errors. |
83 |
class IncompatibleConfigClass(Exception): |
84 |
"""Raised when a client is initialised with the wrong config class."""
|
|
85 |
||
86 |
||
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
87 |
class SoftDeadlineExceeded(Exception): |
88 |
"""Raised when an overall client operation takes too long."""
|
|
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
89 |
|
1647.1.1
by Aaron Bentley
Ignore soft deadline in safe_print_status. |
90 |
def __init__(self): |
91 |
super(SoftDeadlineExceeded, self).__init__( |
|
92 |
'Operation exceeded deadline.') |
|
93 |
||
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
94 |
|
1674.1.1
by Aaron Bentley
Add get_provider to jujupy. |
95 |
class NoProvider(Exception): |
96 |
"""Raised when an environment defines no provider."""
|
|
97 |
||
98 |
||
1727.2.17
by Aaron Bentley
Add bogus type testing. |
99 |
class TypeNotAccepted(Exception): |
100 |
"""Raised when the provided type was not accepted."""
|
|
101 |
||
102 |
||
1727.2.18
by Aaron Bentley
Check invalid name handling. |
103 |
class NameNotAccepted(Exception): |
104 |
"""Raised when the provided name was not accepted."""
|
|
105 |
||
106 |
||
1727.3.1
by Aaron Bentley
add_cloud_interative raises an exception if auth not accepted. |
107 |
class AuthNotAccepted(Exception): |
108 |
"""Raised when the provided auth was not accepted."""
|
|
109 |
||
110 |
||
1069.1.1
by Aaron Bentley
Replace timeout utility in _full_args. |
111 |
def get_timeout_path(): |
112 |
import timeout |
|
113 |
return os.path.abspath(timeout.__file__) |
|
114 |
||
115 |
||
116 |
def get_timeout_prefix(duration, timeout_path=None): |
|
117 |
"""Return extra arguments to run a command with a timeout."""
|
|
118 |
if timeout_path is None: |
|
119 |
timeout_path = get_timeout_path() |
|
120 |
return (sys.executable, timeout_path, '%.2f' % duration, '--') |
|
121 |
||
122 |
||
1535.1.4
by Curtis Hovey
Extracted get_teardown_timeout. |
123 |
def get_teardown_timeout(client): |
124 |
"""Return the timeout need byt the client to teardown resources."""
|
|
1674.1.3
by Aaron Bentley
Use provider rather than get_provider. |
125 |
if client.env.provider == 'azure': |
1535.1.4
by Curtis Hovey
Extracted get_teardown_timeout. |
126 |
return 1800 |
1723
by Curtis Hovey
Give GCE more time to delete large deployments, Lp #1640587 |
127 |
elif client.env.provider == 'gce': |
128 |
return 1200 |
|
1535.1.4
by Curtis Hovey
Extracted get_teardown_timeout. |
129 |
else: |
130 |
return 600 |
|
131 |
||
132 |
||
953.3.8
by Nate Finch
more review changes |
133 |
def parse_new_state_server_from_error(error): |
976.2.1
by Aaron Bentley
Fix parse_new_state_server_from_error |
134 |
err_str = str(error) |
135 |
output = getattr(error, 'output', None) |
|
136 |
if output is not None: |
|
137 |
err_str += output |
|
138 |
matches = re.findall(r'Attempting to connect to (.*):22', err_str) |
|
953.3.8
by Nate Finch
more review changes |
139 |
if matches: |
140 |
return matches[-1] |
|
141 |
return None |
|
142 |
||
143 |
||
2
by Aaron Bentley
Added initial deploy_stack. |
144 |
class ErroredUnit(Exception): |
145 |
||
301.1.3
by Aaron Bentley
Remove environment name from log messages and errors. |
146 |
def __init__(self, unit_name, state): |
147 |
msg = '%s is in state %s' % (unit_name, state) |
|
19.1.14
by Aaron Bentley
More error handling fixes. |
148 |
Exception.__init__(self, msg) |
771.2.1
by Aaron Bentley
Increase set of agent-state-infos that indicate failure. |
149 |
self.unit_name = unit_name |
150 |
self.state = state |
|
2
by Aaron Bentley
Added initial deploy_stack. |
151 |
|
152 |
||
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
153 |
class BootstrapMismatch(Exception): |
154 |
||
155 |
def __init__(self, arg_name, arg_val, env_name, env_val): |
|
156 |
super(BootstrapMismatch, self).__init__( |
|
157 |
'--{} {} does not match {}: {}'.format( |
|
158 |
arg_name, arg_val, env_name, env_val)) |
|
159 |
||
160 |
||
1258.2.5
by Curtis Hovey
Added upgrade_mongo to EnvJujuClient. |
161 |
class UpgradeMongoNotSupported(Exception): |
162 |
||
163 |
def __init__(self): |
|
164 |
super(UpgradeMongoNotSupported, self).__init__( |
|
165 |
'This client does not support upgrade-mongo') |
|
166 |
||
167 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
168 |
class JESNotSupported(Exception): |
169 |
||
170 |
def __init__(self): |
|
171 |
super(JESNotSupported, self).__init__( |
|
1183.1.9
by Curtis Hovey
Revert change that makes diff hard to read. |
172 |
'This client does not support JES') |
1044.1.9
by Aaron Bentley
Update and add tests. |
173 |
|
174 |
||
175 |
class JESByDefault(Exception): |
|
176 |
||
177 |
def __init__(self): |
|
1044.1.13
by Aaron Bentley
Add more enable_jes tests. |
178 |
super(JESByDefault, self).__init__( |
1183.1.9
by Curtis Hovey
Revert change that makes diff hard to read. |
179 |
'This client does not need to enable JES') |
1183.1.2
by Curtis Hovey
Save point. |
180 |
|
181 |
||
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
182 |
class VersionNotTestedError(Exception): |
183 |
||
184 |
def __init__(self, version): |
|
185 |
super(VersionNotTestedError, self).__init__( |
|
186 |
'Tests for juju {} are no longer supported.'.format(version)) |
|
187 |
||
188 |
||
1315.2.1
by Aaron Bentley
Use machine id instead of machine number. |
189 |
Machine = namedtuple('Machine', ['machine_id', 'info']) |
1306.1.7
by Curtis Hovey
Added get_controller_leader. |
190 |
|
191 |
||
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
192 |
def coalesce_agent_status(agent_item): |
193 |
"""Return the machine agent-state or the unit agent-status."""
|
|
194 |
state = agent_item.get('agent-state') |
|
195 |
if state is None and agent_item.get('agent-status') is not None: |
|
196 |
state = agent_item.get('agent-status').get('current') |
|
1293
by Curtis Hovey
Support juju-status. |
197 |
if state is None and agent_item.get('juju-status') is not None: |
198 |
state = agent_item.get('juju-status').get('current') |
|
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
199 |
if state is None: |
200 |
state = 'no-agent' |
|
201 |
return state |
|
202 |
||
203 |
||
185.1.1
by Aaron Bentley
Fix cloud test handling of 'Unable to connect to environment.' |
204 |
class CannotConnectEnv(subprocess.CalledProcessError): |
205 |
||
206 |
def __init__(self, e): |
|
207 |
super(CannotConnectEnv, self).__init__(e.returncode, e.cmd, e.output) |
|
208 |
||
209 |
||
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
210 |
class StatusNotMet(Exception): |
211 |
||
212 |
_fmt = 'Expected status not reached in {env}.' |
|
213 |
||
214 |
def __init__(self, environment_name, status): |
|
215 |
self.env = environment_name |
|
747.3.3
by Aaron Bentley
Implement retrying add-machine. |
216 |
self.status = status |
217 |
||
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
218 |
def __str__(self): |
219 |
return self._fmt.format(env=self.env) |
|
220 |
||
221 |
||
222 |
class AgentsNotStarted(StatusNotMet): |
|
223 |
||
224 |
_fmt = 'Timed out waiting for agents to start in {env}.' |
|
225 |
||
226 |
||
227 |
class VersionsNotUpdated(StatusNotMet): |
|
228 |
||
229 |
_fmt = 'Some versions did not update.' |
|
230 |
||
231 |
||
232 |
class WorkloadsNotReady(StatusNotMet): |
|
233 |
||
234 |
_fmt = 'Workloads not ready in {env}.' |
|
235 |
||
747.3.3
by Aaron Bentley
Implement retrying add-machine. |
236 |
|
1738.2.1
by Andrew Beach
Added StatusNotMet exceptions for wait_for_{deploy_started,ha}. |
237 |
class ApplicationsNotStarted(StatusNotMet): |
238 |
||
239 |
_fmt = 'Timed out waiting for applications to start in {env}.' |
|
240 |
||
241 |
||
242 |
class VotingNotEnabled(StatusNotMet): |
|
243 |
||
244 |
_fmt = 'Timed out waiting for voting to be enabled in {env}.' |
|
245 |
||
246 |
||
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
247 |
@contextmanager
|
248 |
def temp_yaml_file(yaml_dict): |
|
1272.1.1
by Aaron Bentley
temp_yaml_file provides the name of a closed file, for Windows. |
249 |
temp_file = NamedTemporaryFile(suffix='.yaml', delete=False) |
250 |
try: |
|
251 |
with temp_file: |
|
252 |
yaml.safe_dump(yaml_dict, temp_file) |
|
253 |
yield temp_file.name |
|
254 |
finally: |
|
255 |
os.unlink(temp_file.name) |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
256 |
|
257 |
||
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
258 |
class SimpleEnvironment: |
1606.1.1
by Aaron Bentley
Update docs. |
259 |
"""Represents a model in a JUJU_HOME directory for juju 1."""
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
260 |
|
261 |
def __init__(self, environment, config=None, juju_home=None, |
|
262 |
controller=None): |
|
1606.1.1
by Aaron Bentley
Update docs. |
263 |
"""Constructor.
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
264 |
|
265 |
:param environment: Name of the environment.
|
|
266 |
:param config: Dictionary with configuration options, default is None.
|
|
267 |
:param juju_home: Path to JUJU_HOME directory, default is None.
|
|
1606.1.1
by Aaron Bentley
Update docs. |
268 |
:param controller: Controller instance-- this model's controller.
|
269 |
If not given or None a new instance is created."""
|
|
1477.2.12
by Leo Zhang
Fake merge of trunk |
270 |
self.user_name = None |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
271 |
if controller is None: |
272 |
controller = Controller(environment) |
|
273 |
self.controller = controller |
|
274 |
self.environment = environment |
|
275 |
self.config = config |
|
276 |
self.juju_home = juju_home |
|
277 |
if self.config is not None: |
|
1674.1.1
by Aaron Bentley
Add get_provider to jujupy. |
278 |
try: |
1674.1.3
by Aaron Bentley
Use provider rather than get_provider. |
279 |
provider = self.provider |
1674.1.1
by Aaron Bentley
Add get_provider to jujupy. |
280 |
except NoProvider: |
281 |
provider = None |
|
282 |
self.local = bool(provider == 'local') |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
283 |
self.kvm = ( |
284 |
self.local and bool(self.config.get('container') == 'kvm')) |
|
1674.1.1
by Aaron Bentley
Add get_provider to jujupy. |
285 |
self.maas = bool(provider == 'maas') |
286 |
self.joyent = bool(provider == 'joyent') |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
287 |
else: |
288 |
self.local = False |
|
289 |
self.kvm = False |
|
290 |
self.maas = False |
|
291 |
self.joyent = False |
|
292 |
||
1699.1.1
by Aaron Bentley
Implement and use SimpleEnvironment and JujuData get_option. |
293 |
def get_option(self, key, default=None): |
294 |
return self.config.get(key, default) |
|
295 |
||
1674.1.15
by Aaron Bentley
Add update_config method. |
296 |
def update_config(self, new_config): |
297 |
for key, value in new_config.items(): |
|
298 |
if key == 'region': |
|
299 |
logging.warning( |
|
300 |
'Using set_region to set region to "{}".'.format(value)) |
|
301 |
self.set_region(value) |
|
302 |
continue
|
|
303 |
if key == 'type': |
|
304 |
logging.warning('Setting type is not 2.x compatible.') |
|
305 |
self.config[key] = value |
|
306 |
||
1674.1.3
by Aaron Bentley
Use provider rather than get_provider. |
307 |
@property
|
308 |
def provider(self): |
|
1674.1.1
by Aaron Bentley
Add get_provider to jujupy. |
309 |
"""Return the provider type for this environment.
|
310 |
||
311 |
See get_cloud to determine the specific cloud.
|
|
312 |
"""
|
|
313 |
try: |
|
314 |
return self.config['type'] |
|
315 |
except KeyError: |
|
316 |
raise NoProvider('No provider specified.') |
|
317 |
||
1674.1.6
by Aaron Bentley
Move get_region to SimpleEnvironment. |
318 |
def get_region(self): |
319 |
provider = self.provider |
|
320 |
if provider == 'azure': |
|
321 |
if 'tenant-id' not in self.config: |
|
322 |
return self.config['location'].replace(' ', '').lower() |
|
323 |
return self.config['location'] |
|
324 |
elif provider == 'joyent': |
|
325 |
matcher = re.compile('https://(.*).api.joyentcloud.com') |
|
326 |
return matcher.match(self.config['sdc-url']).group(1) |
|
327 |
elif provider == 'lxd': |
|
1699.1.4
by Aaron Bentley
Allow arbitrary regions for lxd. |
328 |
return self.config.get('region', 'localhost') |
1674.1.6
by Aaron Bentley
Move get_region to SimpleEnvironment. |
329 |
elif provider == 'manual': |
330 |
return self.config['bootstrap-host'] |
|
331 |
elif provider in ('maas', 'manual'): |
|
332 |
return None |
|
333 |
else: |
|
334 |
return self.config['region'] |
|
335 |
||
1674.1.7
by Aaron Bentley
Implement SimpleEnvJujuClient.set_region. |
336 |
def set_region(self, region): |
1674.1.8
by Aaron Bentley
set_region works with no provider for testing purposes. |
337 |
try: |
338 |
provider = self.provider |
|
339 |
except NoProvider: |
|
340 |
provider = None |
|
1674.1.7
by Aaron Bentley
Implement SimpleEnvJujuClient.set_region. |
341 |
if provider == 'azure': |
342 |
self.config['location'] = region |
|
343 |
elif provider == 'joyent': |
|
344 |
self.config['sdc-url'] = ( |
|
345 |
'https://{}.api.joyentcloud.com'.format(region)) |
|
346 |
elif provider == 'manual': |
|
347 |
self.config['bootstrap-host'] = region |
|
348 |
elif provider == 'maas': |
|
349 |
if region is not None: |
|
350 |
raise ValueError('Only None allowed for maas.') |
|
351 |
else: |
|
352 |
self.config['region'] = region |
|
353 |
||
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
354 |
def clone(self, model_name=None): |
355 |
config = deepcopy(self.config) |
|
356 |
if model_name is None: |
|
357 |
model_name = self.environment |
|
358 |
else: |
|
1477.3.1
by Andrew Wilkins
beebop |
359 |
config['name'] = unqualified_model_name(model_name) |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
360 |
result = self.__class__(model_name, config, self.juju_home, |
361 |
self.controller) |
|
362 |
result.local = self.local |
|
363 |
result.kvm = self.kvm |
|
364 |
result.maas = self.maas |
|
365 |
result.joyent = self.joyent |
|
366 |
return result |
|
367 |
||
368 |
def __eq__(self, other): |
|
369 |
if type(self) != type(other): |
|
370 |
return False |
|
371 |
if self.environment != other.environment: |
|
372 |
return False |
|
373 |
if self.config != other.config: |
|
374 |
return False |
|
375 |
if self.local != other.local: |
|
376 |
return False |
|
377 |
if self.maas != other.maas: |
|
378 |
return False |
|
379 |
return True |
|
380 |
||
381 |
def __ne__(self, other): |
|
382 |
return not self == other |
|
383 |
||
384 |
def set_model_name(self, model_name, set_controller=True): |
|
385 |
if set_controller: |
|
386 |
self.controller.name = model_name |
|
387 |
self.environment = model_name |
|
1477.3.1
by Andrew Wilkins
beebop |
388 |
self.config['name'] = unqualified_model_name(model_name) |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
389 |
|
390 |
@classmethod
|
|
391 |
def from_config(cls, name): |
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
392 |
"""Create an environment from the configuation file.
|
393 |
||
394 |
:param name: Name of the environment to get the configuration from."""
|
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
395 |
return cls._from_config(name) |
396 |
||
397 |
@classmethod
|
|
398 |
def _from_config(cls, name): |
|
399 |
config, selected = get_selected_environment(name) |
|
400 |
if name is None: |
|
401 |
name = selected |
|
402 |
return cls(name, config) |
|
403 |
||
404 |
def needs_sudo(self): |
|
405 |
return self.local |
|
406 |
||
407 |
@contextmanager
|
|
408 |
def make_jes_home(self, juju_home, dir_name, new_config): |
|
1695.2.9
by Andrew Beach
Updated SimpleEnvironment.make_jes_home in order keep public-clouds.yaml in the home directory. |
409 |
"""Make a JUJU_HOME/DATA directory to avoid conflicts.
|
410 |
||
411 |
:param juju_home: Current JUJU_HOME/DATA directory, used as a
|
|
412 |
base path for the new directory.
|
|
413 |
:param dir_name: Name of sub-directory to make the home in.
|
|
1695.2.11
by Andrew Beach
Update in responce to merge request comments. |
414 |
:param new_config: Dictionary representing the contents of
|
1695.2.9
by Andrew Beach
Updated SimpleEnvironment.make_jes_home in order keep public-clouds.yaml in the home directory. |
415 |
the environments.yaml configuation file."""
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
416 |
home_path = jes_home_path(juju_home, dir_name) |
1703.1.6
by Andrew Beach
Renamed the function to skip_on_missing_file. |
417 |
with skip_on_missing_file(): |
1695.2.12
by Andrew Beach
Fixes for more comments. |
418 |
shutil.rmtree(home_path) |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
419 |
os.makedirs(home_path) |
420 |
self.dump_yaml(home_path, new_config) |
|
1695.2.11
by Andrew Beach
Update in responce to merge request comments. |
421 |
# For extention: Add all files carried over to the list.
|
422 |
for file_name in ['public-clouds.yaml']: |
|
423 |
src_path = os.path.join(juju_home, file_name) |
|
1703.1.6
by Andrew Beach
Renamed the function to skip_on_missing_file. |
424 |
with skip_on_missing_file(): |
1695.2.12
by Andrew Beach
Fixes for more comments. |
425 |
shutil.copy(src_path, home_path) |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
426 |
yield home_path |
427 |
||
1614.2.2
by Aaron Bentley
Implement SimpleEnvironment.get_cloud_credentials. |
428 |
def get_cloud_credentials(self): |
429 |
"""Return the credentials for this model's cloud.
|
|
430 |
||
431 |
This implementation returns config variables in addition to
|
|
432 |
credentials.
|
|
433 |
"""
|
|
434 |
return self.config |
|
435 |
||
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
436 |
def dump_yaml(self, path, config): |
437 |
dump_environments_yaml(path, config) |
|
438 |
||
439 |
||
440 |
class JujuData(SimpleEnvironment): |
|
1606.1.1
by Aaron Bentley
Update docs. |
441 |
"""Represents a model in a JUJU_DATA directory for juju 2."""
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
442 |
|
443 |
def __init__(self, environment, config=None, juju_home=None, |
|
444 |
controller=None): |
|
1606.1.1
by Aaron Bentley
Update docs. |
445 |
"""Constructor.
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
446 |
|
447 |
This extends SimpleEnvironment's constructor.
|
|
448 |
||
1606.1.1
by Aaron Bentley
Update docs. |
449 |
:param environment: Name of the environment.
|
450 |
:param config: Dictionary with configuration options; default is None.
|
|
451 |
:param juju_home: Path to JUJU_DATA directory. If None (the default),
|
|
452 |
the home directory is autodetected.
|
|
453 |
:param controller: Controller instance-- this model's controller.
|
|
454 |
If not given or None, a new instance is created.
|
|
455 |
"""
|
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
456 |
if juju_home is None: |
457 |
juju_home = get_juju_home() |
|
458 |
super(JujuData, self).__init__(environment, config, juju_home, |
|
459 |
controller) |
|
460 |
self.credentials = {} |
|
461 |
self.clouds = {} |
|
462 |
||
463 |
def clone(self, model_name=None): |
|
464 |
result = super(JujuData, self).clone(model_name) |
|
465 |
result.credentials = deepcopy(self.credentials) |
|
466 |
result.clouds = deepcopy(self.clouds) |
|
467 |
return result |
|
468 |
||
469 |
@classmethod
|
|
470 |
def from_env(cls, env): |
|
471 |
juju_data = cls(env.environment, env.config, env.juju_home) |
|
472 |
juju_data.load_yaml() |
|
473 |
return juju_data |
|
474 |
||
1674.1.15
by Aaron Bentley
Add update_config method. |
475 |
def update_config(self, new_config): |
476 |
if 'type' in new_config: |
|
477 |
raise ValueError('type cannot be set via update_config.') |
|
478 |
super(JujuData, self).update_config(new_config) |
|
479 |
||
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
480 |
def load_yaml(self): |
481 |
try: |
|
482 |
with open(os.path.join(self.juju_home, 'credentials.yaml')) as f: |
|
483 |
self.credentials = yaml.safe_load(f) |
|
484 |
except IOError as e: |
|
485 |
if e.errno != errno.ENOENT: |
|
486 |
raise RuntimeError( |
|
487 |
'Failed to read credentials file: {}'.format(str(e))) |
|
488 |
self.credentials = {} |
|
1711.6.10
by Aaron Bentley
Extract and use JujuData.read_clouds. |
489 |
self.clouds = self.read_clouds() |
490 |
||
491 |
def read_clouds(self): |
|
492 |
"""Read and return clouds.yaml as a Python dict."""
|
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
493 |
try: |
494 |
with open(os.path.join(self.juju_home, 'clouds.yaml')) as f: |
|
1711.6.10
by Aaron Bentley
Extract and use JujuData.read_clouds. |
495 |
return yaml.safe_load(f) |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
496 |
except IOError as e: |
497 |
if e.errno != errno.ENOENT: |
|
498 |
raise RuntimeError( |
|
499 |
'Failed to read clouds file: {}'.format(str(e))) |
|
500 |
# Default to an empty clouds file.
|
|
1711.6.10
by Aaron Bentley
Extract and use JujuData.read_clouds. |
501 |
return {'clouds': {}} |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
502 |
|
503 |
@classmethod
|
|
504 |
def from_config(cls, name): |
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
505 |
"""Create a model from the three configuration files."""
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
506 |
juju_data = cls._from_config(name) |
507 |
juju_data.load_yaml() |
|
508 |
return juju_data |
|
509 |
||
510 |
def dump_yaml(self, path, config): |
|
511 |
"""Dump the configuration files to the specified path.
|
|
512 |
||
513 |
config is unused, but is accepted for compatibility with
|
|
514 |
SimpleEnvironment and make_jes_home().
|
|
515 |
"""
|
|
516 |
with open(os.path.join(path, 'credentials.yaml'), 'w') as f: |
|
517 |
yaml.safe_dump(self.credentials, f) |
|
1711.6.12
by Aaron Bentley
Test assess_cloud. |
518 |
self.write_clouds(path, self.clouds) |
519 |
||
520 |
@staticmethod
|
|
521 |
def write_clouds(path, clouds): |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
522 |
with open(os.path.join(path, 'clouds.yaml'), 'w') as f: |
1711.6.12
by Aaron Bentley
Test assess_cloud. |
523 |
yaml.safe_dump(clouds, f) |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
524 |
|
525 |
def find_endpoint_cloud(self, cloud_type, endpoint): |
|
1586.1.3
by Curtis Hovey
Revert find_endpoint_cloud and rmeove dangerous logging. |
526 |
for cloud, cloud_config in self.clouds['clouds'].items(): |
527 |
if cloud_config['type'] != cloud_type: |
|
528 |
continue
|
|
529 |
if cloud_config['endpoint'] == endpoint: |
|
530 |
return cloud |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
531 |
raise LookupError('No such endpoint: {}'.format(endpoint)) |
532 |
||
533 |
def get_cloud(self): |
|
1674.1.3
by Aaron Bentley
Use provider rather than get_provider. |
534 |
provider = self.provider |
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
535 |
# Separate cloud recommended by: Juju Cloud / Credentials / BootStrap /
|
536 |
# Model CLI specification
|
|
537 |
if provider == 'ec2' and self.config['region'] == 'cn-north-1': |
|
538 |
return 'aws-china' |
|
539 |
if provider not in ('maas', 'openstack'): |
|
540 |
return { |
|
541 |
'ec2': 'aws', |
|
542 |
'gce': 'google', |
|
543 |
}.get(provider, provider) |
|
544 |
if provider == 'maas': |
|
545 |
endpoint = self.config['maas-server'] |
|
546 |
elif provider == 'openstack': |
|
547 |
endpoint = self.config['auth-url'] |
|
548 |
return self.find_endpoint_cloud(provider, endpoint) |
|
549 |
||
1711.3.3
by Aaron Bentley
Let controller decide whether to supply cloud/region and credentials. |
550 |
def get_cloud_credentials_item(self): |
1614.2.1
by Aaron Bentley
Extract credential retrieval from assess_autoload_credentials. |
551 |
cloud_name = self.get_cloud() |
552 |
cloud = self.credentials['credentials'][cloud_name] |
|
1711.3.3
by Aaron Bentley
Let controller decide whether to supply cloud/region and credentials. |
553 |
(credentials_item,) = cloud.items() |
554 |
return credentials_item |
|
555 |
||
556 |
def get_cloud_credentials(self): |
|
557 |
"""Return the credentials for this model's cloud."""
|
|
558 |
return self.get_cloud_credentials_item()[1] |
|
1614.2.1
by Aaron Bentley
Extract credential retrieval from assess_autoload_credentials. |
559 |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
560 |
|
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
561 |
class StatusError(Exception): |
562 |
"""Generic error for Status."""
|
|
563 |
||
564 |
recoverable = True |
|
565 |
||
566 |
# This has to be filled in after the classes are declared.
|
|
567 |
ordering = [] |
|
568 |
||
569 |
@classmethod
|
|
570 |
def priority(cls): |
|
571 |
"""Get the priority of the StatusError as an number.
|
|
572 |
||
573 |
Lower number means higher priority. This can be used as a key
|
|
574 |
function in sorting."""
|
|
575 |
return cls.ordering.index(cls) |
|
576 |
||
577 |
||
578 |
class MachineError(StatusError): |
|
579 |
"""Error in machine-status."""
|
|
580 |
||
581 |
recoverable = False |
|
582 |
||
583 |
||
584 |
class UnitError(StatusError): |
|
585 |
"""Error in a unit's status."""
|
|
586 |
||
587 |
||
588 |
class HookFailedError(UnitError): |
|
589 |
"""A unit hook has failed."""
|
|
590 |
||
591 |
def __init__(self, item_name, msg): |
|
592 |
match = re.search('^hook failed: "([^"]+)"$', msg) |
|
593 |
if match: |
|
594 |
msg = match.group(1) |
|
595 |
super(HookFailedError, self).__init__(item_name, msg) |
|
596 |
||
597 |
||
598 |
class InstallError(HookFailedError): |
|
599 |
"""The unit's install hook has failed."""
|
|
600 |
||
601 |
recoverable = False |
|
602 |
||
603 |
||
604 |
class AppError(StatusError): |
|
605 |
"""Error in an application's status."""
|
|
606 |
||
607 |
||
608 |
class AgentError(StatusError): |
|
609 |
"""Error in a juju agent."""
|
|
610 |
||
611 |
||
1726.1.3
by Andrew Beach
Agent{Long -> Unresolved}Error and added some tests for priority. |
612 |
class AgentUnresolvedError(AgentError): |
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
613 |
"""Agent error has not recovered in a reasonable time."""
|
614 |
||
1726.2.6
by Andrew Beach
Added documentation and moved the magic number of 5min out of the calculation. |
615 |
# This is the time limit set by IS for recovery from an agent error.
|
616 |
a_reasonable_time = timedelta(minutes=5) |
|
617 |
||
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
618 |
recoverable = False |
619 |
||
620 |
||
1726.1.3
by Andrew Beach
Agent{Long -> Unresolved}Error and added some tests for priority. |
621 |
StatusError.ordering = [MachineError, InstallError, AgentUnresolvedError, |
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
622 |
HookFailedError, UnitError, AppError, AgentError, |
623 |
StatusError] |
|
624 |
||
625 |
||
626 |
class StatusItem: |
|
627 |
||
628 |
APPLICATION = 'application-status' |
|
629 |
WORKLOAD = 'workload-status' |
|
630 |
MACHINE = 'machine-status' |
|
631 |
JUJU = 'juju-status' |
|
632 |
||
633 |
def __init__(self, status_name, item_name, item_value): |
|
1726.2.6
by Andrew Beach
Added documentation and moved the magic number of 5min out of the calculation. |
634 |
"""Create a new StatusItem from its fields.
|
635 |
||
636 |
:param status_name: One of the status strings.
|
|
637 |
:param item_name: The name of the machine/unit/application the status
|
|
638 |
information is about.
|
|
639 |
:param item_value: A dictionary of status values. If there is an entry
|
|
640 |
with the status_name in the dictionary its contents are used."""
|
|
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
641 |
self.status_name = status_name |
642 |
self.item_name = item_name |
|
1726.2.5
by Andrew Beach
Fixes from comments and added to_exception tests. |
643 |
self.status = item_value.get(status_name, item_value) |
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
644 |
|
645 |
@property
|
|
646 |
def message(self): |
|
647 |
return self.status.get('message') |
|
648 |
||
649 |
@property
|
|
650 |
def since(self): |
|
651 |
return self.status.get('since') |
|
652 |
||
653 |
@property
|
|
654 |
def current(self): |
|
655 |
return self.status.get('current') |
|
656 |
||
657 |
@property
|
|
658 |
def version(self): |
|
659 |
return self.status.get('version') |
|
660 |
||
1726.2.5
by Andrew Beach
Fixes from comments and added to_exception tests. |
661 |
@property
|
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
662 |
def datetime_since(self): |
1726.2.5
by Andrew Beach
Fixes from comments and added to_exception tests. |
663 |
if self.since is None: |
664 |
return None |
|
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
665 |
return datetime.strptime(self.since, '%d %b %Y %H:%M:%SZ') |
666 |
||
667 |
def to_exception(self): |
|
668 |
"""Create an exception representing the error if one exists.
|
|
669 |
||
670 |
:return: StatusError (or subtype) to represent an error or None
|
|
671 |
to show that there is no error."""
|
|
672 |
if self.current not in ['error', 'failed']: |
|
673 |
return None |
|
674 |
||
675 |
if self.APPLICATION == self.status_name: |
|
676 |
return AppError(self.item_name, self.message) |
|
677 |
elif self.WORKLOAD == self.status_name: |
|
678 |
if self.message is None: |
|
679 |
return UnitError(self.item_name, self.message) |
|
1726.2.5
by Andrew Beach
Fixes from comments and added to_exception tests. |
680 |
elif re.match('hook failed: ".*install.*"', self.message): |
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
681 |
return InstallError(self.item_name, self.message) |
682 |
elif re.match('hook failed', self.message): |
|
683 |
return HookFailedError(self.item_name, self.message) |
|
684 |
else: |
|
685 |
return UnitError(self.item_name, self.message) |
|
686 |
elif self.MACHINE == self.status_name: |
|
687 |
return MachineError(self.item_name, self.message) |
|
688 |
elif self.JUJU == self.status_name: |
|
1726.2.5
by Andrew Beach
Fixes from comments and added to_exception tests. |
689 |
time_since = datetime.utcnow() - self.datetime_since |
1726.2.7
by Andrew Beach
Fixed to lint standard. |
690 |
if time_since > AgentUnresolvedError.a_reasonable_time: |
1726.2.4
by Andrew Beach
Update Agent{Long -> Unresolved}Error name. |
691 |
return AgentUnresolvedError(self.item_name, self.message, |
692 |
time_since.total_seconds()) |
|
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
693 |
else: |
694 |
return AgentError(self.item_name, self.message) |
|
695 |
else: |
|
696 |
raise ValueError('Unknown status:{}'.format(self.status_name), |
|
697 |
(self.item_name, self.status_value)) |
|
698 |
||
1726.4.15
by Andrew Beach
It was a testing problem all along. |
699 |
def __repr__(self): |
700 |
return 'StatusItem({!r}, {!r}, {!r})'.format( |
|
701 |
self.status_name, self.item_name, self.status) |
|
702 |
||
1726.1.1
by Andrew Beach
Grabbed changes from fix-wait-for-started. |
703 |
|
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
704 |
class Status: |
705 |
||
706 |
def __init__(self, status, status_text): |
|
707 |
self.status = status |
|
708 |
self.status_text = status_text |
|
709 |
||
710 |
@classmethod
|
|
711 |
def from_text(cls, text): |
|
1437.1.1
by Aaron Bentley
Speed up tests by using JSON for status. |
712 |
try: |
713 |
# Parsing as JSON is much faster than parsing as YAML, so try
|
|
714 |
# parsing as JSON first and fall back to YAML.
|
|
715 |
status_yaml = json.loads(text) |
|
716 |
except ValueError: |
|
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
717 |
status_yaml = yaml.safe_load(text) |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
718 |
return cls(status_yaml, text) |
719 |
||
720 |
def get_applications(self): |
|
721 |
return self.status.get('applications', {}) |
|
722 |
||
1726.3.4
by Andrew Beach
Reflecting some changes from down stream. |
723 |
def get_machines(self, default=None): |
724 |
return self.status.get('machines', default) |
|
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
725 |
|
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
726 |
def iter_machines(self, containers=False, machines=True): |
727 |
for machine_name, machine in sorted(self.status['machines'].items()): |
|
728 |
if machines: |
|
729 |
yield machine_name, machine |
|
730 |
if containers: |
|
731 |
for contained, unit in machine.get('containers', {}).items(): |
|
732 |
yield contained, unit |
|
733 |
||
734 |
def iter_new_machines(self, old_status): |
|
735 |
for machine, data in self.iter_machines(): |
|
736 |
if machine in old_status.status['machines']: |
|
737 |
continue
|
|
738 |
yield machine, data |
|
739 |
||
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
740 |
def _iter_units_in_application(self, app_data): |
741 |
"""Given application data, iterate through every unit in it."""
|
|
742 |
for unit_name, unit in sorted(app_data.get('units', {}).items()): |
|
743 |
yield unit_name, unit |
|
744 |
subordinates = unit.get('subordinates', ()) |
|
745 |
for sub_name in sorted(subordinates): |
|
746 |
yield sub_name, subordinates[sub_name] |
|
747 |
||
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
748 |
def iter_units(self): |
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
749 |
"""Iterate over every unit in every application."""
|
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
750 |
for service_name, service in sorted(self.get_applications().items()): |
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
751 |
for name, data in self._iter_units_in_application(service): |
752 |
yield name, data |
|
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
753 |
|
754 |
def agent_items(self): |
|
755 |
for machine_name, machine in self.iter_machines(containers=True): |
|
756 |
yield machine_name, machine |
|
757 |
for unit_name, unit in self.iter_units(): |
|
758 |
yield unit_name, unit |
|
759 |
||
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
760 |
def unit_agent_states(self, states=None): |
761 |
"""Fill in a dictionary with the states of units.
|
|
762 |
||
763 |
Units of a dying application are marked as dying.
|
|
764 |
||
765 |
:param states: If not None, when it should be a defaultdict(list)),
|
|
766 |
then states are added to this dictionary."""
|
|
767 |
if states is None: |
|
768 |
states = defaultdict(list) |
|
769 |
for app_name, app_data in sorted(self.get_applications().items()): |
|
770 |
if app_data.get('life') == 'dying': |
|
771 |
for unit, data in self._iter_units_in_application(app_data): |
|
772 |
states['dying'].append(unit) |
|
773 |
else: |
|
774 |
for unit, data in self._iter_units_in_application(app_data): |
|
775 |
states[coalesce_agent_status(data)].append(unit) |
|
776 |
return states |
|
777 |
||
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
778 |
def agent_states(self): |
779 |
"""Map agent states to the units and machines in those states."""
|
|
780 |
states = defaultdict(list) |
|
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
781 |
for item_name, item in self.iter_machines(containers=True): |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
782 |
states[coalesce_agent_status(item)].append(item_name) |
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
783 |
self.unit_agent_states(states) |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
784 |
return states |
785 |
||
786 |
def check_agents_started(self, environment_name=None): |
|
787 |
"""Check whether all agents are in the 'started' state.
|
|
788 |
||
789 |
If not, return agent_states output. If so, return None.
|
|
790 |
If an error is encountered for an agent, raise ErroredUnit
|
|
791 |
"""
|
|
792 |
bad_state_info = re.compile( |
|
793 |
'(.*error|^(cannot set up groups|cannot run instance)).*') |
|
794 |
for item_name, item in self.agent_items(): |
|
795 |
state_info = item.get('agent-state-info', '') |
|
796 |
if bad_state_info.match(state_info): |
|
797 |
raise ErroredUnit(item_name, state_info) |
|
798 |
states = self.agent_states() |
|
1629.2.5
by Andrew Beach
Created a new fix for wait-for-started (in check_agents_started) that folds dying into the agent_states. |
799 |
if set(states.keys()).issubset(AGENTS_READY): |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
800 |
return None |
801 |
for state, entries in states.items(): |
|
802 |
if 'error' in state: |
|
1459.2.4
by Nicholas Skaggs
move logic as requested |
803 |
# sometimes the state may be hidden in juju status message
|
1459.2.5
by Nicholas Skaggs
minor tweaks per review |
804 |
juju_status = dict( |
805 |
self.agent_items())[entries[0]].get('juju-status') |
|
1459.2.4
by Nicholas Skaggs
move logic as requested |
806 |
if juju_status: |
807 |
juju_status_msg = juju_status.get('message') |
|
808 |
if juju_status_msg: |
|
1459.2.5
by Nicholas Skaggs
minor tweaks per review |
809 |
state = juju_status_msg |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
810 |
raise ErroredUnit(entries[0], state) |
811 |
return states |
|
812 |
||
813 |
def get_service_count(self): |
|
814 |
return len(self.get_applications()) |
|
815 |
||
816 |
def get_service_unit_count(self, service): |
|
817 |
return len( |
|
818 |
self.get_applications().get(service, {}).get('units', {})) |
|
819 |
||
820 |
def get_agent_versions(self): |
|
821 |
versions = defaultdict(set) |
|
822 |
for item_name, item in self.agent_items(): |
|
823 |
if item.get('juju-status', None): |
|
824 |
version = item['juju-status'].get('version', 'unknown') |
|
825 |
versions[version].add(item_name) |
|
826 |
else: |
|
827 |
versions[item.get('agent-version', 'unknown')].add(item_name) |
|
828 |
return versions |
|
829 |
||
830 |
def get_instance_id(self, machine_id): |
|
831 |
return self.status['machines'][machine_id]['instance-id'] |
|
832 |
||
1631.4.3
by Andrew Beach
Added a check to assess_to to make sure the argument was used. |
833 |
def get_machine_dns_name(self, machine_id): |
834 |
return _dns_name_for_machine(self, machine_id) |
|
835 |
||
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
836 |
def get_unit(self, unit_name): |
837 |
"""Return metadata about a unit."""
|
|
838 |
for service in sorted(self.get_applications().values()): |
|
839 |
if unit_name in service.get('units', {}): |
|
840 |
return service['units'][unit_name] |
|
841 |
raise KeyError(unit_name) |
|
842 |
||
843 |
def service_subordinate_units(self, service_name): |
|
844 |
"""Return subordinate metadata for a service_name."""
|
|
845 |
services = self.get_applications() |
|
846 |
if service_name in services: |
|
847 |
for unit in sorted(services[service_name].get( |
|
848 |
'units', {}).values()): |
|
849 |
for sub_name, sub in unit.get('subordinates', {}).items(): |
|
850 |
yield sub_name, sub |
|
851 |
||
852 |
def get_open_ports(self, unit_name): |
|
853 |
"""List the open ports for the specified unit.
|
|
854 |
||
855 |
If no ports are listed for the unit, the empty list is returned.
|
|
856 |
"""
|
|
857 |
return self.get_unit(unit_name).get('open-ports', []) |
|
858 |
||
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
859 |
def iter_status(self): |
860 |
"""Iterate through every status field in the larger status data."""
|
|
1726.4.1
by Andrew Beach
Tried actually using check_for_errors which highlighted some errors. I had to change a bunch of existing errors just to get meaningful errors out of them. |
861 |
for machine_name, machine_value in self.get_machines({}).items(): |
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
862 |
yield StatusItem(StatusItem.MACHINE, machine_name, machine_value) |
863 |
yield StatusItem(StatusItem.JUJU, machine_name, machine_value) |
|
864 |
for app_name, app_value in self.get_applications().items(): |
|
865 |
yield StatusItem(StatusItem.APPLICATION, app_name, app_value) |
|
1743.1.1
by Andrew Beach
Allows for missing 'units' field in iter_status (both). |
866 |
for unit_name, unit_value in app_value.get('units', {}).items(): |
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
867 |
yield StatusItem(StatusItem.WORKLOAD, unit_name, unit_value) |
868 |
yield StatusItem(StatusItem.JUJU, unit_name, unit_value) |
|
869 |
||
870 |
def iter_errors(self, ignore_recoverable=False): |
|
871 |
"""Iterate through every error, repersented by exceptions."""
|
|
872 |
for sub_status in self.iter_status(): |
|
873 |
error = sub_status.to_exception() |
|
874 |
if error is not None: |
|
875 |
if not (ignore_recoverable and error.recoverable): |
|
876 |
yield error |
|
877 |
||
878 |
def check_for_errors(self, ignore_recoverable=False): |
|
879 |
"""Return a list of errors, in order of their priority."""
|
|
880 |
return sorted(self.iter_errors(ignore_recoverable), |
|
881 |
key=lambda item: item.priority()) |
|
882 |
||
883 |
def raise_highest_error(self, ignore_recoverable=False): |
|
884 |
"""Raise an exception reperenting the highest priority error."""
|
|
885 |
errors = self.check_for_errors(ignore_recoverable) |
|
886 |
if errors: |
|
887 |
raise errors[0] |
|
888 |
||
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
889 |
|
1692.1.1
by Andrew Beach
ServiceStatus->Status1X |
890 |
class Status1X(Status): |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
891 |
|
892 |
def get_applications(self): |
|
893 |
return self.status.get('services', {}) |
|
894 |
||
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
895 |
def condense_status(self, item_value): |
896 |
"""Condense the scattered agent-* fields into a status dict."""
|
|
1726.4.13
by Andrew Beach
There where too many tests to add 'agent-status' and 'agent-version' too, so I had to make them optional. |
897 |
def shift_field(dest_dict, dest_name, src_dict, src_name): |
898 |
if src_name in src_dict: |
|
899 |
dest_dict[dest_name] = src_dict[src_name] |
|
900 |
condensed = {} |
|
901 |
shift_field(condensed, 'current', item_value, 'agent-state') |
|
902 |
shift_field(condensed, 'version', item_value, 'agent-version') |
|
903 |
shift_field(condensed, 'message', item_value, 'agent-state-info') |
|
904 |
return condensed |
|
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
905 |
|
906 |
def iter_status(self): |
|
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
907 |
SERVICE = 'service-status' |
908 |
AGENT = 'agent-status' |
|
1726.4.1
by Andrew Beach
Tried actually using check_for_errors which highlighted some errors. I had to change a bunch of existing errors just to get meaningful errors out of them. |
909 |
for machine_name, machine_value in self.get_machines({}).items(): |
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
910 |
yield StatusItem(StatusItem.JUJU, machine_name, |
911 |
self.condense_status(machine_value)) |
|
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
912 |
for app_name, app_value in self.get_applications().items(): |
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
913 |
if SERVICE in app_value: |
914 |
yield StatusItem( |
|
915 |
StatusItem.APPLICATION, app_name, |
|
916 |
{StatusItem.APPLICATION: app_value[SERVICE]}) |
|
1743.1.1
by Andrew Beach
Allows for missing 'units' field in iter_status (both). |
917 |
for unit_name, unit_value in app_value.get('units', {}).items(): |
1726.4.1
by Andrew Beach
Tried actually using check_for_errors which highlighted some errors. I had to change a bunch of existing errors just to get meaningful errors out of them. |
918 |
if StatusItem.WORKLOAD in unit_value: |
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
919 |
yield StatusItem(StatusItem.WORKLOAD, |
920 |
unit_name, unit_value) |
|
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
921 |
if AGENT in unit_value: |
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
922 |
yield StatusItem( |
923 |
StatusItem.JUJU, unit_name, |
|
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
924 |
{StatusItem.JUJU: unit_value[AGENT]}) |
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
925 |
else: |
1726.3.7
by Andrew Beach
Added tests for the new code in Status1X. |
926 |
yield StatusItem(StatusItem.JUJU, unit_name, |
927 |
self.condense_status(unit_value)) |
|
1726.3.1
by Andrew Beach
Fake merge from status-status-item. |
928 |
|
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
929 |
|
1699.1.3
by Aaron Bentley
Update describe_substrate to take env rather than raw config. |
930 |
def describe_substrate(env): |
931 |
if env.provider == 'local': |
|
932 |
return { |
|
933 |
'kvm': 'KVM (local)', |
|
934 |
'lxc': 'LXC (local)' |
|
935 |
}[env.get_option('container', 'lxc')] |
|
936 |
elif env.provider == 'openstack': |
|
937 |
if env.get_option('auth-url') == ( |
|
938 |
'https://keystone.canonistack.canonical.com:443/v2.0/'): |
|
939 |
return 'Canonistack' |
|
940 |
else: |
|
941 |
return 'Openstack' |
|
942 |
try: |
|
943 |
return { |
|
944 |
'ec2': 'AWS', |
|
945 |
'rackspace': 'Rackspace', |
|
946 |
'joyent': 'Joyent', |
|
947 |
'azure': 'Azure', |
|
948 |
'maas': 'MAAS', |
|
949 |
}[env.provider] |
|
950 |
except KeyError: |
|
951 |
return env.provider |
|
952 |
||
953 |
||
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
954 |
class Juju2Backend: |
1363.7.9
by Aaron Bentley
Clean up backend name and docs. |
955 |
"""A Juju backend referring to a specific juju 2 binary.
|
956 |
||
957 |
Uses -m to specify models, uses JUJU_DATA to specify home directory.
|
|
958 |
"""
|
|
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
959 |
|
1564.2.1
by Aaron Bentley
Add deadline support to full_args. |
960 |
def __init__(self, full_path, version, feature_flags, debug, |
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
961 |
soft_deadline=None): |
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
962 |
self._version = version |
963 |
self._full_path = full_path |
|
1363.10.11
by Aaron Bentley
Handle feature flags in clone. |
964 |
self.feature_flags = feature_flags |
1363.7.4
by Aaron Bentley
Move debug to backend. |
965 |
self.debug = debug |
1363.7.3
by Aaron Bentley
Move _timeout_path to backend. |
966 |
self._timeout_path = get_timeout_path() |
1363.8.1
by Aaron Bentley
Move juju_timings to backend. |
967 |
self.juju_timings = {} |
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
968 |
self.soft_deadline = soft_deadline |
969 |
self._ignore_soft_deadline = False |
|
1564.2.1
by Aaron Bentley
Add deadline support to full_args. |
970 |
|
971 |
def _now(self): |
|
972 |
return datetime.utcnow() |
|
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
973 |
|
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
974 |
@contextmanager
|
975 |
def _check_timeouts(self): |
|
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
976 |
# If an exception occurred, we don't want to replace it with
|
977 |
# SoftDeadlineExceeded.
|
|
978 |
yield
|
|
979 |
if self.soft_deadline is None or self._ignore_soft_deadline: |
|
980 |
return
|
|
981 |
if self._now() > self.soft_deadline: |
|
1647.1.1
by Aaron Bentley
Ignore soft deadline in safe_print_status. |
982 |
raise SoftDeadlineExceeded() |
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
983 |
|
984 |
@contextmanager
|
|
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
985 |
def ignore_soft_deadline(self): |
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
986 |
"""Ignore the client deadline. For cleanup code."""
|
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
987 |
old_val = self._ignore_soft_deadline |
988 |
self._ignore_soft_deadline = True |
|
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
989 |
try: |
990 |
yield
|
|
991 |
finally: |
|
1564.2.3
by Aaron Bentley
Switch to soft deadline, handle as an exception. |
992 |
self._ignore_soft_deadline = old_val |
1564.2.2
by Aaron Bentley
Add check_timeouts and ignore_deadline. |
993 |
|
1363.10.11
by Aaron Bentley
Handle feature flags in clone. |
994 |
def clone(self, full_path, version, debug, feature_flags): |
1363.9.6
by Aaron Bentley
Tweak cloning. |
995 |
if version is None: |
996 |
version = self.version |
|
997 |
if full_path is None: |
|
998 |
full_path = self.full_path |
|
999 |
if debug is None: |
|
1000 |
debug = self.debug |
|
1575.2.9
by Aaron Bentley
Retain soft_deadline when cloning. |
1001 |
result = self.__class__(full_path, version, feature_flags, debug, |
1002 |
self.soft_deadline) |
|
1363.9.6
by Aaron Bentley
Tweak cloning. |
1003 |
return result |
1004 |
||
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
1005 |
@property
|
1006 |
def version(self): |
|
1007 |
return self._version |
|
1008 |
||
1009 |
@property
|
|
1010 |
def full_path(self): |
|
1011 |
return self._full_path |
|
1012 |
||
1433.1.1
by Aaron Bentley
Use supplied juju binary name. |
1013 |
@property
|
1014 |
def juju_name(self): |
|
1015 |
return os.path.basename(self._full_path) |
|
1016 |
||
1363.8.1
by Aaron Bentley
Move juju_timings to backend. |
1017 |
def _get_attr_tuple(self): |
1018 |
return (self._version, self._full_path, self.feature_flags, |
|
1019 |
self.debug, self.juju_timings) |
|
1020 |
||
1363.6.5
by Aaron Bentley
Store feature flags on backend. |
1021 |
def __eq__(self, other): |
1022 |
if type(self) != type(other): |
|
1023 |
return False |
|
1363.8.1
by Aaron Bentley
Move juju_timings to backend. |
1024 |
return self._get_attr_tuple() == other._get_attr_tuple() |
1363.6.5
by Aaron Bentley
Store feature flags on backend. |
1025 |
|
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
1026 |
def shell_environ(self, used_feature_flags, juju_home): |
1027 |
"""Generate a suitable shell environment.
|
|
1028 |
||
1029 |
Juju's directory must be in the PATH to support plugins.
|
|
1030 |
"""
|
|
1031 |
env = dict(os.environ) |
|
1032 |
if self.full_path is not None: |
|
1033 |
env['PATH'] = '{}{}{}'.format(os.path.dirname(self.full_path), |
|
1034 |
os.pathsep, env['PATH']) |
|
1035 |
flags = self.feature_flags.intersection(used_feature_flags) |
|
1717.1.1
by Andrew Beach
Added code and tests for keeping existing values of JUJU_DEV_FEATURE_FLAGS in a shell_environ. |
1036 |
feature_flag_string = env.get(JUJU_DEV_FEATURE_FLAGS, '') |
1037 |
if feature_flag_string != '': |
|
1038 |
flags.update(feature_flag_string.split(',')) |
|
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
1039 |
if flags: |
1040 |
env[JUJU_DEV_FEATURE_FLAGS] = ','.join(sorted(flags)) |
|
1041 |
env['JUJU_DATA'] = juju_home |
|
1042 |
return env |
|
1043 |
||
1363.7.4
by Aaron Bentley
Move debug to backend. |
1044 |
def full_args(self, command, args, model, timeout): |
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1045 |
if model is not None: |
1046 |
e_arg = ('-m', model) |
|
1047 |
else: |
|
1048 |
e_arg = () |
|
1049 |
if timeout is None: |
|
1050 |
prefix = () |
|
1051 |
else: |
|
1363.7.3
by Aaron Bentley
Move _timeout_path to backend. |
1052 |
prefix = get_timeout_prefix(timeout, self._timeout_path) |
1363.7.4
by Aaron Bentley
Move debug to backend. |
1053 |
logging = '--debug' if self.debug else '--show-log' |
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1054 |
|
1055 |
# If args is a string, make it a tuple. This makes writing commands
|
|
1056 |
# with one argument a bit nicer.
|
|
1057 |
if isinstance(args, basestring): |
|
1058 |
args = (args,) |
|
1059 |
# we split the command here so that the caller can control where the -m
|
|
1060 |
# model flag goes. Everything in the command string is put before the
|
|
1061 |
# -m flag.
|
|
1062 |
command = command.split() |
|
1433.1.1
by Aaron Bentley
Use supplied juju binary name. |
1063 |
return (prefix + (self.juju_name, logging,) + tuple(command) + e_arg + |
1064 |
args) |
|
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1065 |
|
1363.8.2
by Aaron Bentley
Move juju implementation to backend. |
1066 |
def juju(self, command, args, used_feature_flags, |
1067 |
juju_home, model=None, check=True, timeout=None, extra_env=None): |
|
1068 |
"""Run a command under juju for the current environment."""
|
|
1069 |
args = self.full_args(command, args, model, timeout) |
|
1070 |
log.info(' '.join(args)) |
|
1071 |
env = self.shell_environ(used_feature_flags, juju_home) |
|
1072 |
if extra_env is not None: |
|
1073 |
env.update(extra_env) |
|
1074 |
if check: |
|
1075 |
call_func = subprocess.check_call |
|
1076 |
else: |
|
1077 |
call_func = subprocess.call |
|
1078 |
start_time = time.time() |
|
1079 |
# Mutate os.environ instead of supplying env parameter so Windows can
|
|
1080 |
# search env['PATH']
|
|
1081 |
with scoped_environ(env): |
|
1564.2.4
by Aaron Bentley
Implement soft deadline on basic juju operations. |
1082 |
with self._check_timeouts(): |
1083 |
rval = call_func(args) |
|
1363.8.2
by Aaron Bentley
Move juju implementation to backend. |
1084 |
self.juju_timings.setdefault(args, []).append( |
1085 |
(time.time() - start_time)) |
|
1086 |
return rval |
|
1087 |
||
1410.2.4
by Christopher Lee
Fix EnvJujuClient.expect to ensure it uses the provided path within envvar. |
1088 |
def expect(self, command, args, used_feature_flags, juju_home, model=None, |
1089 |
timeout=None, extra_env=None): |
|
1090 |
args = self.full_args(command, args, model, timeout) |
|
1091 |
log.info(' '.join(args)) |
|
1092 |
env = self.shell_environ(used_feature_flags, juju_home) |
|
1093 |
if extra_env is not None: |
|
1094 |
env.update(extra_env) |
|
1095 |
# pexpect.spawn expects a string. This is better than trying to extract
|
|
1096 |
# command + args from the returned tuple (as there could be an intial
|
|
1097 |
# timing command tacked on).
|
|
1098 |
command_string = ' '.join(quote(a) for a in args) |
|
1099 |
with scoped_environ(env): |
|
1100 |
return pexpect.spawn(command_string) |
|
1101 |
||
1363.8.8
by Aaron Bentley
Move juju_async to backend. |
1102 |
@contextmanager
|
1103 |
def juju_async(self, command, args, used_feature_flags, |
|
1104 |
juju_home, model=None, timeout=None): |
|
1105 |
full_args = self.full_args(command, args, model, timeout) |
|
1106 |
log.info(' '.join(args)) |
|
1107 |
env = self.shell_environ(used_feature_flags, juju_home) |
|
1108 |
# Mutate os.environ instead of supplying env parameter so Windows can
|
|
1109 |
# search env['PATH']
|
|
1110 |
with scoped_environ(env): |
|
1564.2.4
by Aaron Bentley
Implement soft deadline on basic juju operations. |
1111 |
with self._check_timeouts(): |
1112 |
proc = subprocess.Popen(full_args) |
|
1363.8.8
by Aaron Bentley
Move juju_async to backend. |
1113 |
yield proc |
1114 |
retcode = proc.wait() |
|
1115 |
if retcode != 0: |
|
1116 |
raise subprocess.CalledProcessError(retcode, full_args) |
|
1117 |
||
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
1118 |
def get_juju_output(self, command, args, used_feature_flags, juju_home, |
1119 |
model=None, timeout=None, user_name=None, |
|
1120 |
merge_stderr=False): |
|
1363.8.6
by Aaron Bentley
Implement get_juju_output on backend. |
1121 |
args = self.full_args(command, args, model, timeout) |
1122 |
env = self.shell_environ(used_feature_flags, juju_home) |
|
1123 |
log.debug(args) |
|
1124 |
# Mutate os.environ instead of supplying env parameter so
|
|
1125 |
# Windows can search env['PATH']
|
|
1126 |
with scoped_environ(env): |
|
1127 |
proc = subprocess.Popen( |
|
1128 |
args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, |
|
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
1129 |
stderr=subprocess.STDOUT if merge_stderr else subprocess.PIPE) |
1564.2.4
by Aaron Bentley
Implement soft deadline on basic juju operations. |
1130 |
with self._check_timeouts(): |
1131 |
sub_output, sub_error = proc.communicate() |
|
1363.8.6
by Aaron Bentley
Implement get_juju_output on backend. |
1132 |
log.debug(sub_output) |
1133 |
if proc.returncode != 0: |
|
1134 |
log.debug(sub_error) |
|
1135 |
e = subprocess.CalledProcessError( |
|
1136 |
proc.returncode, args, sub_output) |
|
1137 |
e.stderr = sub_error |
|
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
1138 |
if sub_error and ( |
1363.8.6
by Aaron Bentley
Implement get_juju_output on backend. |
1139 |
'Unable to connect to environment' in sub_error or |
1140 |
'MissingOrIncorrectVersionHeader' in sub_error or |
|
1141 |
'307: Temporary Redirect' in sub_error): |
|
1142 |
raise CannotConnectEnv(e) |
|
1143 |
raise e |
|
1144 |
return sub_output |
|
1145 |
||
1363.9.2
by Aaron Bentley
Move pause to backend. |
1146 |
def pause(self, seconds): |
1147 |
pause(seconds) |
|
1148 |
||
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
1149 |
|
1662.1.6
by Andrew Beach
Removed Juju2A2Backend. |
1150 |
class Juju1XBackend(Juju2Backend): |
1151 |
"""Backend for Juju 1.x versions.
|
|
1363.7.9
by Aaron Bentley
Clean up backend name and docs. |
1152 |
|
1153 |
Uses -e to specify models ("environments", uses JUJU_HOME to specify home
|
|
1154 |
directory.
|
|
1155 |
"""
|
|
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
1156 |
|
1157 |
def shell_environ(self, used_feature_flags, juju_home): |
|
1158 |
"""Generate a suitable shell environment.
|
|
1159 |
||
1160 |
For 2.0-alpha1 and earlier set only JUJU_HOME and not JUJU_DATA.
|
|
1161 |
"""
|
|
1363.7.9
by Aaron Bentley
Clean up backend name and docs. |
1162 |
env = super(Juju1XBackend, self).shell_environ(used_feature_flags, |
1163 |
juju_home) |
|
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
1164 |
env['JUJU_HOME'] = juju_home |
1165 |
del env['JUJU_DATA'] |
|
1166 |
return env |
|
1167 |
||
1363.7.4
by Aaron Bentley
Move debug to backend. |
1168 |
def full_args(self, command, args, model, timeout): |
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1169 |
if model is None: |
1170 |
e_arg = () |
|
1171 |
else: |
|
1172 |
# In 1.x terminology, "model" is "environment".
|
|
1173 |
e_arg = ('-e', model) |
|
1174 |
if timeout is None: |
|
1175 |
prefix = () |
|
1176 |
else: |
|
1363.7.3
by Aaron Bentley
Move _timeout_path to backend. |
1177 |
prefix = get_timeout_prefix(timeout, self._timeout_path) |
1363.7.4
by Aaron Bentley
Move debug to backend. |
1178 |
logging = '--debug' if self.debug else '--show-log' |
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1179 |
|
1180 |
# If args is a string, make it a tuple. This makes writing commands
|
|
1181 |
# with one argument a bit nicer.
|
|
1182 |
if isinstance(args, basestring): |
|
1183 |
args = (args,) |
|
1184 |
# we split the command here so that the caller can control where the -e
|
|
1185 |
# <env> flag goes. Everything in the command string is put before the
|
|
1186 |
# -e flag.
|
|
1187 |
command = command.split() |
|
1433.1.1
by Aaron Bentley
Use supplied juju binary name. |
1188 |
return (prefix + (self.juju_name, logging,) + tuple(command) + e_arg + |
1189 |
args) |
|
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1190 |
|
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
1191 |
|
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1192 |
def get_client_class(version): |
1193 |
if version.startswith('1.16'): |
|
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
1194 |
raise VersionNotTestedError(version) |
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1195 |
elif re.match('^1\.22[.-]', version): |
1196 |
client_class = EnvJujuClient22 |
|
1197 |
elif re.match('^1\.24[.-]', version): |
|
1198 |
client_class = EnvJujuClient24 |
|
1199 |
elif re.match('^1\.25[.-]', version): |
|
1200 |
client_class = EnvJujuClient25 |
|
1201 |
elif re.match('^1\.26[.-]', version): |
|
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
1202 |
raise VersionNotTestedError(version) |
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1203 |
elif re.match('^1\.', version): |
1204 |
client_class = EnvJujuClient1X |
|
1662.1.16
by Andrew Beach
Used JESNotSupported to remove unsupported functionality from EnvJujuClient1X. |
1205 |
elif re.match('^2\.0-(alpha|beta)', version): |
1662.1.4
by Andrew Beach
Deleted EnvJujuClient2A1 and removed support for testing with it. |
1206 |
raise VersionNotTestedError(version) |
1646.1.1
by Christopher Lee
Update order of bootstrap args for 2.0+ (RC clients included, w/ test). Updated tests. |
1207 |
elif re.match('^2\.0-rc[1-3]', version): |
1208 |
client_class = EnvJujuClientRC |
|
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1209 |
else: |
1210 |
client_class = EnvJujuClient |
|
1211 |
return client_class |
|
1212 |
||
1213 |
||
1575.2.1
by Aaron Bentley
Shove dealine from arg down to client_from_config. |
1214 |
def client_from_config(config, juju_path, debug=False, soft_deadline=None): |
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1215 |
"""Create a client from an environment's configuration.
|
1216 |
||
1217 |
:param config: Name of the environment to use the config from.
|
|
1218 |
:param juju_path: Path to juju binary the client should wrap.
|
|
1575.3.7
by Aaron Bentley
Update docs. |
1219 |
:param debug=False: The debug flag for the client, False by default.
|
1220 |
:param soft_deadline: A datetime representing the deadline by which
|
|
1221 |
normal operations should complete. If None, no deadline is
|
|
1222 |
enforced.
|
|
1223 |
"""
|
|
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1224 |
version = EnvJujuClient.get_version(juju_path) |
1225 |
client_class = get_client_class(version) |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
1226 |
env = client_class.config_class.from_config(config) |
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1227 |
if juju_path is None: |
1228 |
full_path = EnvJujuClient.get_full_path() |
|
1229 |
else: |
|
1230 |
full_path = os.path.abspath(juju_path) |
|
1575.2.1
by Aaron Bentley
Shove dealine from arg down to client_from_config. |
1231 |
return client_class(env, version, full_path, debug=debug, |
1232 |
soft_deadline=soft_deadline) |
|
1465.5.1
by Aaron Bentley
Implement client_from_config in terms of EnvJujuClient.by_version. |
1233 |
|
1234 |
||
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
1235 |
class EnvJujuClient: |
1600.1.2
by Andrew Beach
Various spelling mistakes. |
1236 |
"""Wraps calls to a juju instance, associated with a single model.
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1237 |
|
1606.1.1
by Aaron Bentley
Update docs. |
1238 |
Note: A model is often called an enviroment (Juju 1 legacy).
|
1239 |
||
1240 |
This class represents the latest Juju version. Subclasses are used to
|
|
1241 |
support older versions (see get_client_class).
|
|
1242 |
"""
|
|
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
1243 |
|
1251.2.4
by Aaron Bentley
Rename bootstrap_supports => bootstrap_replaces |
1244 |
# The environments.yaml options that are replaced by bootstrap options.
|
1245 |
#
|
|
1242.3.8
by Aaron Bentley
Override default-series even when --bootstrap-series is supplied. |
1246 |
# As described in bug #1538735, default-series and --bootstrap-series must
|
1251.2.5
by Aaron Bentley
Tweak verbiage. |
1247 |
# match. 'default-series' should be here, but is omitted so that
|
1248 |
# default-series is always forced to match --bootstrap-series.
|
|
1251.2.4
by Aaron Bentley
Rename bootstrap_supports => bootstrap_replaces |
1249 |
bootstrap_replaces = frozenset(['agent-version']) |
1242.3.7
by Aaron Bentley
BootstrapManager support for bootstrap option. |
1250 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1251 |
# What feature flags have existed that CI used.
|
1662.1.3
by Andrew Beach
Removed the 'address-allocation' and 'cloudsigma' feature flags. |
1252 |
known_feature_flags = frozenset(['actions', 'jes', 'migration']) |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1253 |
|
1254 |
# What feature flags are used by this version of the juju client.
|
|
1662.1.3
by Andrew Beach
Removed the 'address-allocation' and 'cloudsigma' feature flags. |
1255 |
used_feature_flags = frozenset(['migration']) |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1256 |
|
1402.3.8
by Leo Zhang
More updates after review |
1257 |
destroy_model_command = 'destroy-model' |
1258 |
||
1341.2.1
by Aaron Bentley
Move container support knowledge to jujupy. |
1259 |
supported_container_types = frozenset([KVM_MACHINE, LXC_MACHINE, |
1260 |
LXD_MACHINE]) |
|
1261 |
||
1363.7.10
by Aaron Bentley
Use default_backend instead of overriding __init__. |
1262 |
default_backend = Juju2Backend |
1263 |
||
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
1264 |
config_class = JujuData |
1265 |
||
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
1266 |
status_class = Status |
1267 |
||
1540.3.2
by Christopher Lee
Change tools_ to agent_ |
1268 |
agent_metadata_url = 'agent-metadata-url' |
1540.3.1
by Christopher Lee
Update upgrade methods to work with juju 2.0. Upgrades controller first then any other hosted models. |
1269 |
|
1541.2.8
by Curtis Hovey
Remove Use set-model-config instrad of deploy. |
1270 |
model_permissions = frozenset(['read', 'write', 'admin']) |
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
1271 |
|
1541.2.8
by Curtis Hovey
Remove Use set-model-config instrad of deploy. |
1272 |
controller_permissions = frozenset(['login', 'addmodel', 'superuser']) |
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
1273 |
|
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1274 |
reserved_spaces = frozenset([ |
1275 |
'endpoint-bindings-data', 'endpoint-bindings-public']) |
|
1276 |
||
1725.1.1
by Curtis Hovey
Rename disable_command_* to command_set_*. |
1277 |
command_set_destroy_model = 'destroy-model' |
1278 |
||
1279 |
command_set_remove_object = 'remove-object' |
|
1280 |
||
1281 |
command_set_all = 'all' |
|
1723.1.1
by Curtis Hovey
Updated assess_block and jujupy to supprt juju 1x blocks. |
1282 |
|
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1283 |
@classmethod
|
1284 |
def preferred_container(cls): |
|
1285 |
for container_type in [LXD_MACHINE, LXC_MACHINE]: |
|
1286 |
if container_type in cls.supported_container_types: |
|
1287 |
return container_type |
|
1288 |
||
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
1289 |
_show_status = 'show-status' |
1290 |
||
657.1.7
by Aaron Bentley
Clean-up. |
1291 |
@classmethod
|
650.1.9
by Aaron Bentley
Merged trunk into compatibility-test. |
1292 |
def get_version(cls, juju_path=None): |
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1293 |
"""Get the version data from a juju binary.
|
1294 |
||
1606.1.1
by Aaron Bentley
Update docs. |
1295 |
:param juju_path: Path to binary. If not given or None, 'juju' is used.
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1296 |
"""
|
650.1.1
by Aaron Bentley
Add juju_path to from_config |
1297 |
if juju_path is None: |
1298 |
juju_path = 'juju' |
|
1299 |
return subprocess.check_output((juju_path, '--version')).strip() |
|
657.1.7
by Aaron Bentley
Clean-up. |
1300 |
|
1564.2.6
by Aaron Bentley
Ignore soft deadlines for tearing down runtime_context. |
1301 |
def check_timeouts(self): |
1302 |
return self._backend._check_timeouts() |
|
1303 |
||
1304 |
def ignore_soft_deadline(self): |
|
1305 |
return self._backend.ignore_soft_deadline() |
|
1306 |
||
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1307 |
def enable_feature(self, flag): |
1308 |
"""Enable juju feature by setting the given flag.
|
|
1309 |
||
1310 |
New versions of juju with the feature enabled by default will silently
|
|
1311 |
allow this call, but will not export the environment variable.
|
|
1312 |
"""
|
|
1313 |
if flag not in self.known_feature_flags: |
|
1314 |
raise ValueError('Unknown feature flag: %r' % (flag,)) |
|
1315 |
self.feature_flags.add(flag) |
|
1316 |
||
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
1317 |
def get_jes_command(self): |
1318 |
"""For Juju 2.0, this is always kill-controller."""
|
|
1319 |
return KILL_CONTROLLER |
|
1320 |
||
1145.2.11
by Curtis Hovey
Let the client set permanent based on client.is_jes_enabled. |
1321 |
def is_jes_enabled(self): |
1145.2.2
by Curtis Hovey
Extracted get_jes_command from is_jes_enabled. |
1322 |
"""Does the state-server support multiple environments."""
|
1145.2.6
by Curtis Hovey
Raise JESNotSupported from get_jes_command when jes is not supported. |
1323 |
try: |
1145.2.7
by Curtis Hovey
Remove get_jes_command cache. Restored test that simulated the change in help output caused by the jes feature flag. |
1324 |
self.get_jes_command() |
1145.2.6
by Curtis Hovey
Raise JESNotSupported from get_jes_command when jes is not supported. |
1325 |
return True |
1326 |
except JESNotSupported: |
|
1327 |
return False |
|
1145.2.2
by Curtis Hovey
Extracted get_jes_command from is_jes_enabled. |
1328 |
|
1162.2.21
by Aaron Bentley
Implement and use tear_down(try_jes=True) |
1329 |
def enable_jes(self): |
1330 |
"""Enable JES if JES is optional.
|
|
1331 |
||
1332 |
Specifically implemented by the clients that optionally support JES.
|
|
1333 |
This version raises either JESByDefault or JESNotSupported.
|
|
1334 |
||
1335 |
:raises: JESByDefault when JES is always enabled; Juju has the
|
|
1336 |
'destroy-controller' command.
|
|
1337 |
:raises: JESNotSupported when JES is not supported; Juju does not have
|
|
1338 |
the 'system kill' command when the JES feature flag is set.
|
|
1339 |
"""
|
|
1340 |
if self.is_jes_enabled(): |
|
1341 |
raise JESByDefault() |
|
1342 |
else: |
|
1343 |
raise JESNotSupported() |
|
1344 |
||
657.1.7
by Aaron Bentley
Clean-up. |
1345 |
@classmethod
|
1346 |
def get_full_path(cls): |
|
161
by Curtis Hovey
Windows and py3 compatability. |
1347 |
if sys.platform == 'win32': |
163
by Curtis Hovey
Extracted the windows command incase it needs to be reused. |
1348 |
return WIN_JUJU_CMD |
107.1.2
by Aaron Bentley
Use full path when running under sudo. |
1349 |
return subprocess.check_output(('which', 'juju')).rstrip('\n') |
19.1.18
by Aaron Bentley
Provide destroy-environment script to simplify code. |
1350 |
|
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1351 |
def clone_path_cls(self, juju_path): |
1352 |
"""Clone using the supplied path to determine the class."""
|
|
1353 |
version = self.get_version(juju_path) |
|
1354 |
cls = get_client_class(version) |
|
1465.1.1
by Aaron Bentley
Implement client_from_config. |
1355 |
if juju_path is None: |
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1356 |
full_path = self.get_full_path() |
1465.1.1
by Aaron Bentley
Implement client_from_config. |
1357 |
else: |
1358 |
full_path = os.path.abspath(juju_path) |
|
1465.5.2
by Aaron Bentley
Implement client_from_config, delete by_version. |
1359 |
return self.clone(version=version, full_path=full_path, cls=cls) |
1465.1.1
by Aaron Bentley
Implement client_from_config. |
1360 |
|
1221.1.9
by Aaron Bentley
Further extend clone(). |
1361 |
def clone(self, env=None, version=None, full_path=None, debug=None, |
1362 |
cls=None): |
|
1363 |
"""Create a clone of this EnvJujuClient.
|
|
1364 |
||
1365 |
By default, the class, environment, version, full_path, and debug
|
|
1366 |
settings will match the original, but each can be overridden.
|
|
1367 |
"""
|
|
1221.1.8
by Aaron Bentley
Implement EnvJujuClient.clone. |
1368 |
if env is None: |
1369 |
env = self.env |
|
1221.1.9
by Aaron Bentley
Further extend clone(). |
1370 |
if cls is None: |
1371 |
cls = self.__class__ |
|
1363.10.11
by Aaron Bentley
Handle feature flags in clone. |
1372 |
feature_flags = self.feature_flags.intersection(cls.used_feature_flags) |
1373 |
backend = self._backend.clone(full_path, version, debug, feature_flags) |
|
1363.9.6
by Aaron Bentley
Tweak cloning. |
1374 |
other = cls.from_backend(backend, env) |
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1375 |
other.excluded_spaces = set(self.excluded_spaces) |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1376 |
return other |
1221.1.8
by Aaron Bentley
Implement EnvJujuClient.clone. |
1377 |
|
1363.9.6
by Aaron Bentley
Tweak cloning. |
1378 |
@classmethod
|
1379 |
def from_backend(cls, backend, env): |
|
1363.9.7
by Aaron Bentley
FakeJujuClient.clone uses real from_backend. |
1380 |
return cls(env=env, version=backend.version, |
1381 |
full_path=backend.full_path, |
|
1363.9.6
by Aaron Bentley
Tweak cloning. |
1382 |
debug=backend.debug, _backend=backend) |
1383 |
||
1240.2.1
by Aaron Bentley
Support models/cache.yaml |
1384 |
def get_cache_path(self): |
1385 |
return get_cache_path(self.env.juju_home, models=True) |
|
1386 |
||
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1387 |
def _cmd_model(self, include_e, controller): |
1388 |
if controller: |
|
1468.1.1
by Christopher Lee
Fix cmd_model case where admin=True |
1389 |
return '{controller}:{model}'.format( |
1390 |
controller=self.env.controller.name, |
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1391 |
model=self.get_controller_model_name()) |
1363.8.8
by Aaron Bentley
Move juju_async to backend. |
1392 |
elif self.env is None or not include_e: |
1393 |
return None |
|
1394 |
else: |
|
1427.1.1
by Christopher Lee
Update commands to use <controller>:<model> format. Fix arg to -c to be controller name. |
1395 |
return '{controller}:{model}'.format( |
1396 |
controller=self.env.controller.name, |
|
1397 |
model=self.model_name) |
|
1363.8.8
by Aaron Bentley
Move juju_async to backend. |
1398 |
|
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
1399 |
def _full_args(self, command, sudo, args, |
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1400 |
timeout=None, include_e=True, controller=False): |
1401 |
model = self._cmd_model(include_e, controller) |
|
1363.7.2
by Aaron Bentley
Move shell_environ to Backend. |
1402 |
# sudo is not needed for devel releases.
|
1363.7.4
by Aaron Bentley
Move debug to backend. |
1403 |
return self._backend.full_args(command, args, model, timeout) |
19.1.17
by Aaron Bentley
Isolate client to support incompatible command line changes. |
1404 |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1405 |
@staticmethod
|
1406 |
def _get_env(env): |
|
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1407 |
if not isinstance(env, JujuData) and isinstance(env, |
1408 |
SimpleEnvironment): |
|
1260.2.23
by Aaron Bentley
Test SimpleEnvironment/JujuData on init. |
1409 |
# FIXME: JujuData should be used from the start.
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1410 |
env = JujuData.from_env(env) |
1411 |
return env |
|
1412 |
||
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
1413 |
def __init__(self, env, version, full_path, juju_home=None, debug=False, |
1575.2.1
by Aaron Bentley
Shove dealine from arg down to client_from_config. |
1414 |
soft_deadline=None, _backend=None): |
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1415 |
"""Create a new juju client.
|
1416 |
||
1606.1.1
by Aaron Bentley
Update docs. |
1417 |
Required Arguments
|
1418 |
:param env: Object representing a model in a data directory.
|
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1419 |
:param version: Version of juju the client wraps.
|
1420 |
:param full_path: Full path to juju binary.
|
|
1421 |
||
1606.1.1
by Aaron Bentley
Update docs. |
1422 |
Optional Arguments
|
1423 |
:param juju_home: default value for env.juju_home. Will be
|
|
1424 |
autodetected if None (the default).
|
|
1425 |
:param debug: Flag to activate debugging output; False by default.
|
|
1575.3.7
by Aaron Bentley
Update docs. |
1426 |
:param soft_deadline: A datetime representing the deadline by which
|
1427 |
normal operations should complete. If None, no deadline is
|
|
1428 |
enforced.
|
|
1606.1.1
by Aaron Bentley
Update docs. |
1429 |
:param _backend: The backend to use for interacting with the client.
|
1430 |
If None (the default), self.default_backend will be used.
|
|
1431 |
"""
|
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1432 |
self.env = self._get_env(env) |
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
1433 |
if _backend is None: |
1575.2.1
by Aaron Bentley
Shove dealine from arg down to client_from_config. |
1434 |
_backend = self.default_backend(full_path, version, set(), debug, |
1435 |
soft_deadline) |
|
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
1436 |
self._backend = _backend |
1437 |
if version != _backend.version: |
|
1438 |
raise ValueError('Version mismatch: {} {}'.format( |
|
1439 |
version, _backend.version)) |
|
1440 |
if full_path != _backend.full_path: |
|
1441 |
raise ValueError('Path mismatch: {} {}'.format( |
|
1442 |
full_path, _backend.full_path)) |
|
1363.7.4
by Aaron Bentley
Move debug to backend. |
1443 |
if debug is not _backend.debug: |
1444 |
raise ValueError('debug mismatch: {} {}'.format( |
|
1445 |
debug, _backend.debug)) |
|
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
1446 |
if env is not None: |
1447 |
if juju_home is None: |
|
1448 |
if env.juju_home is None: |
|
1449 |
env.juju_home = get_juju_home() |
|
1450 |
else: |
|
1451 |
env.juju_home = juju_home |
|
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1452 |
self.excluded_spaces = set(self.reserved_spaces) |
657.1.7
by Aaron Bentley
Clean-up. |
1453 |
|
1363.6.4
by Aaron Bentley
Store full path and version on Juju2Backend. |
1454 |
@property
|
1455 |
def version(self): |
|
1456 |
return self._backend.version |
|
1457 |
||
1458 |
@property
|
|
1459 |
def full_path(self): |
|
1460 |
return self._backend.full_path |
|
1461 |
||
1363.6.5
by Aaron Bentley
Store feature flags on backend. |
1462 |
@property
|
1463 |
def feature_flags(self): |
|
1464 |
return self._backend.feature_flags |
|
1465 |
||
1466 |
@feature_flags.setter |
|
1467 |
def feature_flags(self, feature_flags): |
|
1468 |
self._backend.feature_flags = feature_flags |
|
1469 |
||
1363.7.4
by Aaron Bentley
Move debug to backend. |
1470 |
@property
|
1471 |
def debug(self): |
|
1472 |
return self._backend.debug |
|
1473 |
||
1363.8.4
by Aaron Bentley
Move model_name to EnvJujuClient. |
1474 |
@property
|
1475 |
def model_name(self): |
|
1476 |
return self.env.environment |
|
1477 |
||
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
1478 |
def _shell_environ(self): |
653
by Aaron Bentley
Add assess-bootstrap. |
1479 |
"""Generate a suitable shell environment.
|
1480 |
||
1481 |
Juju's directory must be in the PATH to support plugins.
|
|
1482 |
"""
|
|
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
1483 |
return self._backend.shell_environ(self.used_feature_flags, |
1484 |
self.env.juju_home) |
|
652
by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery |
1485 |
|
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1486 |
def use_reserved_spaces(self, spaces): |
1487 |
"""Allow machines in given spaces to be allocated and used."""
|
|
1488 |
if not self.reserved_spaces.issuperset(spaces): |
|
1489 |
raise ValueError('Space not reserved: {}'.format(spaces)) |
|
1490 |
self.excluded_spaces.difference_update(spaces) |
|
1491 |
||
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
1492 |
def add_ssh_machines(self, machines): |
1256.1.1
by Aaron Bentley
Retry initial failed add-machine if needed. |
1493 |
for count, machine in enumerate(machines): |
1494 |
try: |
|
1495 |
self.juju('add-machine', ('ssh:' + machine,)) |
|
1496 |
except subprocess.CalledProcessError: |
|
1497 |
if count != 0: |
|
1498 |
raise
|
|
1499 |
logging.warning('add-machine failed. Will retry.') |
|
1500 |
pause(30) |
|
1501 |
self.juju('add-machine', ('ssh:' + machine,)) |
|
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
1502 |
|
1260.2.5
by Aaron Bentley
Split up cloud and region determination. |
1503 |
@staticmethod
|
1504 |
def get_cloud_region(cloud, region): |
|
1260.2.4
by Aaron Bentley
Implement openstack + maas support. |
1505 |
if region is None: |
1506 |
return cloud |
|
1507 |
return '{}/{}'.format(cloud, region) |
|
1508 |
||
1614.1.21
by Andrew Beach
New argument on get_bootstrap_args. New helpers in assess_bootstrap.py and almost finished the tests for them. |
1509 |
def get_bootstrap_args( |
1510 |
self, upload_tools, config_filename, bootstrap_series=None, |
|
1511 |
credential=None, auto_upgrade=False, metadata_source=None, |
|
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
1512 |
to=None, no_gui=False, agent_version=None): |
1260.2.4
by Aaron Bentley
Implement openstack + maas support. |
1513 |
"""Return the bootstrap arguments for the substrate."""
|
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1514 |
constraints = self._get_substrate_constraints() |
1260.2.24
by Aaron Bentley
Move cloud/location to JujuData, add tests, fix lint. |
1515 |
cloud_region = self.get_cloud_region(self.env.get_cloud(), |
1516 |
self.env.get_region()) |
|
1646.1.1
by Christopher Lee
Update order of bootstrap args for 2.0+ (RC clients included, w/ test). Updated tests. |
1517 |
# Note cloud_region before controller name
|
1518 |
args = ['--constraints', constraints, |
|
1519 |
cloud_region, |
|
1520 |
self.env.environment, |
|
1521 |
'--config', config_filename, |
|
1315.2.2
by Aaron Bentley
Use --default-model in bootstrap. |
1522 |
'--default-model', self.env.environment] |
1614.1.24
by Andrew Beach
Added checks to make sure the versions that don't support the new bootstrap arguments don't get them. |
1523 |
if upload_tools: |
1614.1.25
by Andrew Beach
Last bit of pollish. |
1524 |
if agent_version is not None: |
1525 |
raise ValueError( |
|
1526 |
'agent-version may not be given with upload-tools.') |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1527 |
args.insert(0, '--upload-tools') |
1614.1.25
by Andrew Beach
Last bit of pollish. |
1528 |
else: |
1529 |
if agent_version is None: |
|
1530 |
agent_version = self.get_matching_agent_version() |
|
1614.1.21
by Andrew Beach
New argument on get_bootstrap_args. New helpers in assess_bootstrap.py and almost finished the tests for them. |
1531 |
args.extend(['--agent-version', agent_version]) |
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
1532 |
if bootstrap_series is not None: |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1533 |
args.extend(['--bootstrap-series', bootstrap_series]) |
1579.1.6
by Andrew Beach
Added credential argument to EnvJujuClient.bootstrap and EnvJujuClient.get_bootstrap_args. |
1534 |
if credential is not None: |
1535 |
args.extend(['--credential', credential]) |
|
1614.1.2
by Andrew Beach
Added three more arguments and tests for EnvJujuClient.bootstrap. Went for the control method. I hope we don't end up testing every argument this way. |
1536 |
if metadata_source is not None: |
1537 |
args.extend(['--metadata-source', metadata_source]) |
|
1538 |
if auto_upgrade: |
|
1539 |
args.append('--auto-upgrade') |
|
1540 |
if to is not None: |
|
1541 |
args.extend(['--to', to]) |
|
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
1542 |
if no_gui: |
1543 |
args.append('--no-gui') |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1544 |
return tuple(args) |
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
1545 |
|
1363.4.4
by Aaron Bentley
Rename create_model to add_model. |
1546 |
def add_model(self, env): |
1606.1.1
by Aaron Bentley
Update docs. |
1547 |
"""Add a model to this model's controller and return its client.
|
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
1548 |
|
1606.1.1
by Aaron Bentley
Update docs. |
1549 |
:param env: Class representing the new model/environment."""
|
1363.10.1
by Aaron Bentley
Remove cruft from _backing_state. |
1550 |
model_client = self.clone(env) |
1551 |
with model_client._bootstrap_config() as config_file: |
|
1363.4.4
by Aaron Bentley
Rename create_model to add_model. |
1552 |
self._add_model(env.environment, config_file) |
1363.10.1
by Aaron Bentley
Remove cruft from _backing_state. |
1553 |
return model_client |
1363.4.1
by Aaron Bentley
Re-jigger model configuration. |
1554 |
|
1555 |
def make_model_config(self): |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1556 |
config_dict = make_safe_config(self) |
1466.1.1
by Aaron Bentley
Update make_model_config to use agent-metadata-url. |
1557 |
agent_metadata_url = config_dict.pop('tools-metadata-url', None) |
1558 |
if agent_metadata_url is not None: |
|
1559 |
config_dict.setdefault('agent-metadata-url', agent_metadata_url) |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1560 |
# Strip unneeded variables.
|
1363.4.1
by Aaron Bentley
Re-jigger model configuration. |
1561 |
return dict((k, v) for k, v in config_dict.items() if k not in { |
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
1562 |
'access-key', |
1325.1.1
by Aaron Bentley
Exclude admin-secret from configs. |
1563 |
'admin-secret', |
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
1564 |
'application-id', |
1565 |
'application-password', |
|
1566 |
'auth-url', |
|
1280.1.1
by Aaron Bentley
Supply bootstrap host as manual region. |
1567 |
'bootstrap-host', |
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
1568 |
'client-email', |
1569 |
'client-id', |
|
1570 |
'control-bucket', |
|
1571 |
'location', |
|
1572 |
'maas-oauth', |
|
1573 |
'maas-server', |
|
1397.1.2
by Aaron Bentley
Strip old-azure credentials from bootstrap config. |
1574 |
'management-certificate', |
1575 |
'management-subscription-id', |
|
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
1576 |
'manta-key-id', |
1577 |
'manta-user', |
|
1578 |
'name', |
|
1579 |
'password', |
|
1580 |
'private-key', |
|
1581 |
'region', |
|
1582 |
'sdc-key-id', |
|
1583 |
'sdc-url', |
|
1584 |
'sdc-user', |
|
1585 |
'secret-key', |
|
1586 |
'storage-account-name', |
|
1587 |
'subscription-id', |
|
1588 |
'tenant-id', |
|
1589 |
'tenant-name', |
|
1590 |
'type', |
|
1591 |
'username', |
|
1306.1.1
by Curtis Hovey
Save spike to get leader and followers. |
1592 |
})
|
1363.4.1
by Aaron Bentley
Re-jigger model configuration. |
1593 |
|
1594 |
@contextmanager
|
|
1595 |
def _bootstrap_config(self): |
|
1596 |
with temp_yaml_file(self.make_model_config()) as config_filename: |
|
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1597 |
yield config_filename |
1598 |
||
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
1599 |
def _check_bootstrap(self): |
1600 |
if self.env.environment != self.env.controller.name: |
|
1601 |
raise AssertionError( |
|
1602 |
'Controller and environment names should not vary (yet)') |
|
1603 |
||
1522.2.3
by Christopher Lee
Tie new changes to beta15 and above. |
1604 |
def update_user_name(self): |
1631.2.2
by Christopher Lee
Further removal of @local, affecting assess_resources. |
1605 |
self.env.user_name = 'admin' |
1522.2.3
by Christopher Lee
Tie new changes to beta15 and above. |
1606 |
|
1579.1.6
by Andrew Beach
Added credential argument to EnvJujuClient.bootstrap and EnvJujuClient.get_bootstrap_args. |
1607 |
def bootstrap(self, upload_tools=False, bootstrap_series=None, |
1614.1.2
by Andrew Beach
Added three more arguments and tests for EnvJujuClient.bootstrap. Went for the control method. I hope we don't end up testing every argument this way. |
1608 |
credential=None, auto_upgrade=False, metadata_source=None, |
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
1609 |
to=None, no_gui=False, agent_version=None): |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1610 |
"""Bootstrap a controller."""
|
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
1611 |
self._check_bootstrap() |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1612 |
with self._bootstrap_config() as config_filename: |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1613 |
args = self.get_bootstrap_args( |
1614.1.2
by Andrew Beach
Added three more arguments and tests for EnvJujuClient.bootstrap. Went for the control method. I hope we don't end up testing every argument this way. |
1614 |
upload_tools, config_filename, bootstrap_series, credential, |
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
1615 |
auto_upgrade, metadata_source, to, no_gui, agent_version) |
1522.2.3
by Christopher Lee
Tie new changes to beta15 and above. |
1616 |
self.update_user_name() |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1617 |
self.juju('bootstrap', args, include_e=False) |
1618 |
||
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
1619 |
@contextmanager
|
1614.1.2
by Andrew Beach
Added three more arguments and tests for EnvJujuClient.bootstrap. Went for the control method. I hope we don't end up testing every argument this way. |
1620 |
def bootstrap_async(self, upload_tools=False, bootstrap_series=None, |
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
1621 |
auto_upgrade=False, metadata_source=None, to=None, |
1622 |
no_gui=False): |
|
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
1623 |
self._check_bootstrap() |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1624 |
with self._bootstrap_config() as config_filename: |
1625 |
args = self.get_bootstrap_args( |
|
1614.1.2
by Andrew Beach
Added three more arguments and tests for EnvJujuClient.bootstrap. Went for the control method. I hope we don't end up testing every argument this way. |
1626 |
upload_tools, config_filename, bootstrap_series, None, |
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
1627 |
auto_upgrade, metadata_source, to, no_gui) |
1522.2.3
by Christopher Lee
Tie new changes to beta15 and above. |
1628 |
self.update_user_name() |
1260.2.20
by Aaron Bentley
Get all tests passing. |
1629 |
with self.juju_async('bootstrap', args, include_e=False): |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1630 |
yield
|
1631 |
log.info('Waiting for bootstrap of {}.'.format( |
|
1632 |
self.env.environment)) |
|
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
1633 |
|
1363.4.4
by Aaron Bentley
Rename create_model to add_model. |
1634 |
def _add_model(self, model_name, config_file): |
1711.3.3
by Aaron Bentley
Let controller decide whether to supply cloud/region and credentials. |
1635 |
explicit_region = self.env.controller.explicit_region |
1636 |
if explicit_region: |
|
1637 |
credential_name = self.env.get_cloud_credentials_item()[0] |
|
1638 |
cloud_region = self.get_cloud_region(self.env.get_cloud(), |
|
1639 |
self.env.get_region()) |
|
1711.3.7
by Aaron Bentley
Fix add-model argument order. |
1640 |
region_args = (cloud_region, '--credential', credential_name) |
1711.3.3
by Aaron Bentley
Let controller decide whether to supply cloud/region and credentials. |
1641 |
else: |
1642 |
region_args = () |
|
1711.3.7
by Aaron Bentley
Fix add-model argument order. |
1643 |
self.controller_juju('add-model', (model_name,) + region_args + ( |
1644 |
'--config', config_file)) |
|
1162.2.2
by Aaron Bentley
Switch to direct BootstrapManager. |
1645 |
|
1221.1.15
by Aaron Bentley
Implement and use destroy_model for destroying models. |
1646 |
def destroy_model(self): |
1210.1.1
by Aaron Bentley
Destroy environment without --force if possible. |
1647 |
exit_status = self.juju( |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1648 |
'destroy-model', (self.env.environment, '-y',), |
1535.1.4
by Curtis Hovey
Extracted get_teardown_timeout. |
1649 |
include_e=False, timeout=get_teardown_timeout(self)) |
1210.1.1
by Aaron Bentley
Destroy environment without --force if possible. |
1650 |
return exit_status |
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
1651 |
|
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
1652 |
def kill_controller(self): |
1688.1.16
by Andrew Beach
Updated doc-strings. |
1653 |
"""Kill a controller and its models. Hard kill option.
|
1654 |
||
1655 |
:return: Subprocess's exit code."""
|
|
1685.1.2
by Andrew Beach
Updated the tests for kill_controller and destroy_environment. |
1656 |
return self.juju( |
1657 |
'kill-controller', (self.env.controller.name, '-y'), |
|
1535.1.4
by Curtis Hovey
Extracted get_teardown_timeout. |
1658 |
include_e=False, check=False, timeout=get_teardown_timeout(self)) |
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
1659 |
|
1688.1.15
by Andrew Beach
Added --destroy-all-models flag to destroy-controller. |
1660 |
def destroy_controller(self, all_models=False): |
1688.1.16
by Andrew Beach
Updated doc-strings. |
1661 |
"""Destroy a controller and its models. Soft kill option.
|
1662 |
||
1663 |
:param all_models: If true will attempt to destroy all the
|
|
1664 |
controller's models as well.
|
|
1665 |
:raises: subprocess.CalledProcessError if the operation fails."""
|
|
1688.1.15
by Andrew Beach
Added --destroy-all-models flag to destroy-controller. |
1666 |
args = (self.env.controller.name, '-y') |
1667 |
if all_models: |
|
1668 |
args += ('--destroy-all-models',) |
|
1669 |
return self.juju('destroy-controller', args, include_e=False, |
|
1670 |
timeout=get_teardown_timeout(self)) |
|
1685.1.4
by Andrew Beach
Added the soft kill destroy-controller. |
1671 |
|
1688.1.13
by Andrew Beach
Fake Merge. |
1672 |
def tear_down(self): |
1673 |
"""Tear down the client as cleanly as possible.
|
|
1674 |
||
1675 |
Attempts to use the soft method destroy_controller, if that fails
|
|
1688.1.15
by Andrew Beach
Added --destroy-all-models flag to destroy-controller. |
1676 |
it will use the hard kill_controller and raise an error."""
|
1688.1.13
by Andrew Beach
Fake Merge. |
1677 |
try: |
1688.1.15
by Andrew Beach
Added --destroy-all-models flag to destroy-controller. |
1678 |
self.destroy_controller(all_models=True) |
1688.1.13
by Andrew Beach
Fake Merge. |
1679 |
except subprocess.CalledProcessError: |
1680 |
logging.warning('tear_down destroy-controller failed') |
|
1681 |
retval = self.kill_controller() |
|
1682 |
message = 'tear_down kill-controller result={}'.format(retval) |
|
1683 |
if retval == 0: |
|
1684 |
logging.info(message) |
|
1685 |
else: |
|
1686 |
logging.warning(message) |
|
1687 |
raise
|
|
1688 |
||
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
1689 |
def get_juju_output(self, command, *args, **kwargs): |
953.3.13
by Nate Finch
more code review changes |
1690 |
"""Call a juju command and return the output.
|
1691 |
||
1692 |
Sub process will be called as 'juju <command> <args> <kwargs>'. Note
|
|
1693 |
that <command> may be a space delimited list of arguments. The -e
|
|
953.3.15
by Aaron Bentley
Fix lint. |
1694 |
<environment> flag will be placed after <command> and before args.
|
953.3.13
by Nate Finch
more code review changes |
1695 |
"""
|
1363.8.8
by Aaron Bentley
Move juju_async to backend. |
1696 |
model = self._cmd_model(kwargs.get('include_e', True), |
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1697 |
kwargs.get('controller', False)) |
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
1698 |
pass_kwargs = dict( |
1699 |
(k, kwargs[k]) for k in kwargs if k in ['timeout', 'merge_stderr']) |
|
1477.2.6
by Leo Zhang
New updates after review |
1700 |
return self._backend.get_juju_output( |
1701 |
command, args, self.used_feature_flags, self.env.juju_home, |
|
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
1702 |
model, user_name=self.env.user_name, **pass_kwargs) |
19.1.18
by Aaron Bentley
Provide destroy-environment script to simplify code. |
1703 |
|
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
1704 |
def show_status(self): |
1705 |
"""Print the status to output."""
|
|
1246.1.4
by Curtis Hovey
Safe print yaml status to ensure continuity. |
1706 |
self.juju(self._show_status, ('--format', 'yaml')) |
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
1707 |
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1708 |
def get_status(self, timeout=60, raw=False, controller=False, *args): |
19.1.17
by Aaron Bentley
Isolate client to support incompatible command line changes. |
1709 |
"""Get the current status as a dict."""
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1710 |
# GZ 2015-12-16: Pass remaining timeout into get_juju_output call.
|
419.1.13
by Aaron Bentley
Allow 10 minutes for status to start working again. |
1711 |
for ignored in until_timeout(timeout): |
331.1.1
by Aaron Bentley
juju status retries on error for 30 seconds. |
1712 |
try: |
979.3.5
by Horacio Durán
Addressed curtis observations. |
1713 |
if raw: |
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
1714 |
return self.get_juju_output(self._show_status, *args) |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
1715 |
return self.status_class.from_text( |
1246.1.1
by Curtis Hovey
Call status with --format yaml. |
1716 |
self.get_juju_output( |
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1717 |
self._show_status, '--format', 'yaml', |
1718 |
controller=controller)) |
|
1091.5.4
by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors. |
1719 |
except subprocess.CalledProcessError: |
331.1.1
by Aaron Bentley
juju status retries on error for 30 seconds. |
1720 |
pass
|
1721 |
raise Exception( |
|
1091.5.4
by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors. |
1722 |
'Timed out waiting for juju status to succeed') |
19.1.17
by Aaron Bentley
Isolate client to support incompatible command line changes. |
1723 |
|
1718.1.15
by Christopher Lee
Move show-model code into JujuEnvClient. |
1724 |
def show_model(self, model_name=None): |
1725 |
model_details = self.get_juju_output( |
|
1726 |
'show-model', |
|
1727 |
'{}:{}'.format( |
|
1728 |
self.env.controller.name, model_name or self.env.environment), |
|
1729 |
'--format', 'yaml', |
|
1730 |
include_e=False) |
|
1731 |
return yaml.safe_load(model_details) |
|
1732 |
||
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
1733 |
@staticmethod
|
1734 |
def _dict_as_option_strings(options): |
|
1735 |
return tuple('{}={}'.format(*item) for item in options.items()) |
|
1736 |
||
1239.1.1
by Aaron Bentley
Implement and use EnvJujuClient.set_config. |
1737 |
def set_config(self, service, options): |
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
1738 |
option_strings = self._dict_as_option_strings(options) |
1588.1.1
by Curtis Hovey
Remove set-/set- from config command. |
1739 |
self.juju('config', (service,) + option_strings) |
1239.1.1
by Aaron Bentley
Implement and use EnvJujuClient.set_config. |
1740 |
|
1221.5.11
by Aaron Bentley
Extract EnvJujuClient.get_config. |
1741 |
def get_config(self, service): |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
1742 |
return yaml.safe_load(self.get_juju_output('config', service)) |
1221.5.11
by Aaron Bentley
Extract EnvJujuClient.get_config. |
1743 |
|
979.2.1
by John George
Add support for checking chaos is complete, from run_chaos_monkey.py |
1744 |
def get_service_config(self, service, timeout=60): |
1745 |
for ignored in until_timeout(timeout): |
|
1746 |
try: |
|
1221.5.11
by Aaron Bentley
Extract EnvJujuClient.get_config. |
1747 |
return self.get_config(service) |
979.2.1
by John George
Add support for checking chaos is complete, from run_chaos_monkey.py |
1748 |
except subprocess.CalledProcessError: |
1749 |
pass
|
|
1750 |
raise Exception( |
|
1751 |
'Timed out waiting for juju get %s' % (service)) |
|
1752 |
||
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
1753 |
def set_model_constraints(self, constraints): |
1754 |
constraint_strings = self._dict_as_option_strings(constraints) |
|
1755 |
return self.juju('set-model-constraints', constraint_strings) |
|
1756 |
||
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
1757 |
def get_model_config(self): |
1583.1.1
by Curtis Hovey
Rename get/set/unset model-config calls to match 2.0-beta18. |
1758 |
"""Return the value of the environment's configured options."""
|
1542.1.1
by Christopher Lee
Add initial test for model config tree. Includes fix for get_model_config |
1759 |
return yaml.safe_load( |
1583.1.1
by Curtis Hovey
Rename get/set/unset model-config calls to match 2.0-beta18. |
1760 |
self.get_juju_output('model-config', '--format', 'yaml')) |
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
1761 |
|
657.1.7
by Aaron Bentley
Clean-up. |
1762 |
def get_env_option(self, option): |
1763 |
"""Return the value of the environment's configured option."""
|
|
1583.1.1
by Curtis Hovey
Rename get/set/unset model-config calls to match 2.0-beta18. |
1764 |
return self.get_juju_output('model-config', option) |
657.1.7
by Aaron Bentley
Clean-up. |
1765 |
|
1766 |
def set_env_option(self, option, value): |
|
1767 |
"""Set the value of the option in the environment."""
|
|
1768 |
option_value = "%s=%s" % (option, value) |
|
1583.1.1
by Curtis Hovey
Rename get/set/unset model-config calls to match 2.0-beta18. |
1769 |
return self.juju('model-config', (option_value,)) |
657.1.7
by Aaron Bentley
Clean-up. |
1770 |
|
1542.1.1
by Christopher Lee
Add initial test for model config tree. Includes fix for get_model_config |
1771 |
def unset_env_option(self, option): |
1772 |
"""Unset the value of the option in the environment."""
|
|
1583.1.1
by Curtis Hovey
Rename get/set/unset model-config calls to match 2.0-beta18. |
1773 |
return self.juju('model-config', ('--reset', option,)) |
1542.1.1
by Christopher Lee
Add initial test for model config tree. Includes fix for get_model_config |
1774 |
|
1540.3.2
by Christopher Lee
Change tools_ to agent_ |
1775 |
def get_agent_metadata_url(self): |
1776 |
return self.get_env_option(self.agent_metadata_url) |
|
1540.3.1
by Christopher Lee
Update upgrade methods to work with juju 2.0. Upgrades controller first then any other hosted models. |
1777 |
|
1540.3.2
by Christopher Lee
Change tools_ to agent_ |
1778 |
def set_testing_agent_metadata_url(self): |
1779 |
url = self.get_agent_metadata_url() |
|
880.1.17
by Aaron Bentley
Fake merge of trunk into no-environment. |
1780 |
if 'testing' not in url: |
1781 |
testing_url = url.replace('/tools', '/testing/tools') |
|
1540.3.2
by Christopher Lee
Change tools_ to agent_ |
1782 |
self.set_env_option(self.agent_metadata_url, testing_url) |
880.1.17
by Aaron Bentley
Fake merge of trunk into no-environment. |
1783 |
|
768.1.1
by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment. |
1784 |
def juju(self, command, args, sudo=False, check=True, include_e=True, |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
1785 |
timeout=None, extra_env=None): |
657.1.7
by Aaron Bentley
Clean-up. |
1786 |
"""Run a command under juju for the current environment."""
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1787 |
model = self._cmd_model(include_e, controller=False) |
1363.8.2
by Aaron Bentley
Move juju implementation to backend. |
1788 |
return self._backend.juju( |
1789 |
command, args, self.used_feature_flags, self.env.juju_home, |
|
1790 |
model, check, timeout, extra_env) |
|
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
1791 |
|
1401.1.4
by Christopher Lee
Add JujuEnvClient.expect with test. Update assess test to use new functionality. |
1792 |
def expect(self, command, args=(), sudo=False, include_e=True, |
1793 |
timeout=None, extra_env=None): |
|
1794 |
"""Return a process object that is running an interactive `command`.
|
|
1795 |
||
1796 |
The interactive command ability is provided by using pexpect.
|
|
1797 |
||
1798 |
:param command: String of the juju command to run.
|
|
1401.2.4
by Christopher Lee
Fix typos and incorrect docstring |
1799 |
:param args: Tuple containing arguments for the juju `command`.
|
1401.1.4
by Christopher Lee
Add JujuEnvClient.expect with test. Update assess test to use new functionality. |
1800 |
:param sudo: Whether to call `command` using sudo.
|
1801 |
:param include_e: Boolean regarding supplying the juju environment to
|
|
1802 |
`command`.
|
|
1803 |
:param timeout: A float that, if provided, is the timeout in which the
|
|
1804 |
`command` is run.
|
|
1805 |
||
1806 |
:return: A pexpect.spawn object that has been called with `command` and
|
|
1807 |
`args`.
|
|
1808 |
||
1809 |
"""
|
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1810 |
model = self._cmd_model(include_e, controller=False) |
1410.2.4
by Christopher Lee
Fix EnvJujuClient.expect to ensure it uses the provided path within envvar. |
1811 |
return self._backend.expect( |
1812 |
command, args, self.used_feature_flags, self.env.juju_home, |
|
1813 |
model, timeout, extra_env) |
|
1401.1.4
by Christopher Lee
Add JujuEnvClient.expect with test. Update assess test to use new functionality. |
1814 |
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
1815 |
def controller_juju(self, command, args): |
1816 |
args = ('-c', self.env.controller.name) + args |
|
1817 |
return self.juju(command, args, include_e=False) |
|
1818 |
||
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
1819 |
def get_juju_timings(self): |
1820 |
stringified_timings = {} |
|
1363.8.1
by Aaron Bentley
Move juju_timings to backend. |
1821 |
for command, timings in self._backend.juju_timings.items(): |
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
1822 |
stringified_timings[' '.join(command)] = timings |
1823 |
return stringified_timings |
|
657.1.7
by Aaron Bentley
Clean-up. |
1824 |
|
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
1825 |
def juju_async(self, command, args, include_e=True, timeout=None): |
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
1826 |
model = self._cmd_model(include_e, controller=False) |
1363.8.8
by Aaron Bentley
Move juju_async to backend. |
1827 |
return self._backend.juju_async(command, args, self.used_feature_flags, |
1828 |
self.env.juju_home, model, timeout) |
|
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
1829 |
|
1345.1.3
by Seman
Deploy charm by path. |
1830 |
def deploy(self, charm, repository=None, to=None, series=None, |
1566.1.1
by Leo Zhang
Add assess_constraints |
1831 |
service=None, force=False, resource=None, |
1832 |
storage=None, constraints=None): |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1833 |
args = [charm] |
1389.1.1
by Aaron Bentley
Use a timeout of 60 for lxd removal. |
1834 |
if service is not None: |
1835 |
args.extend([service]) |
|
1014.2.1
by John George
background_chaos WIP |
1836 |
if to is not None: |
1837 |
args.extend(['--to', to]) |
|
1337.1.1
by Reed O'Brien
This adds the `--series` flag to the deploy client wrapper. It allows |
1838 |
if series is not None: |
1839 |
args.extend(['--series', series]) |
|
1344.3.3
by Nicholas Skaggs
shell in place |
1840 |
if force is True: |
1841 |
args.extend(['--force']) |
|
1410.1.1
by Seman
Added CI test for deploying a charm with resources. |
1842 |
if resource is not None: |
1843 |
args.extend(['--resource', resource]) |
|
1426.1.1
by Leo Zhang
Add assess_storage |
1844 |
if storage is not None: |
1845 |
args.extend(['--storage', storage]) |
|
1566.1.1
by Leo Zhang
Add assess_constraints |
1846 |
if constraints is not None: |
1847 |
args.extend(['--constraints', constraints]) |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1848 |
return self.juju('deploy', tuple(args)) |
650.1.12
by Aaron Bentley
Rename to assess-foreign, update to use EnvJujuClient. |
1849 |
|
1410.1.1
by Seman
Added CI test for deploying a charm with resources. |
1850 |
def attach(self, service, resource): |
1851 |
args = (service, resource) |
|
1852 |
return self.juju('attach', args) |
|
1853 |
||
1854 |
def list_resources(self, service_or_unit, details=True): |
|
1855 |
args = ('--format', 'yaml', service_or_unit) |
|
1856 |
if details: |
|
1857 |
args = args + ('--details',) |
|
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
1858 |
return yaml.safe_load(self.get_juju_output('list-resources', *args)) |
1410.1.1
by Seman
Added CI test for deploying a charm with resources. |
1859 |
|
1417.1.1
by Seman
Added more resource CI tests. |
1860 |
def wait_for_resource(self, resource_id, service_or_unit, timeout=60): |
1861 |
log.info('Waiting for resource. Resource id:{}'.format(resource_id)) |
|
1575.1.3
by Aaron Bentley
Handle soft deadlines for wait_for_resource. |
1862 |
with self.check_timeouts(): |
1863 |
with self.ignore_soft_deadline(): |
|
1864 |
for _ in until_timeout(timeout): |
|
1865 |
resources_dict = self.list_resources(service_or_unit) |
|
1866 |
resources = resources_dict['resources'] |
|
1867 |
for resource in resources: |
|
1868 |
if resource['expected']['resourceid'] == resource_id: |
|
1869 |
if (resource['expected']['fingerprint'] == |
|
1870 |
resource['unit']['fingerprint']): |
|
1871 |
return
|
|
1872 |
time.sleep(.1) |
|
1873 |
raise JujuResourceTimeout( |
|
1874 |
'Timeout waiting for a resource to be downloaded. '
|
|
1875 |
'ResourceId: {} Service or Unit: {} Timeout: {}'.format( |
|
1876 |
resource_id, service_or_unit, timeout)) |
|
1417.1.1
by Seman
Added more resource CI tests. |
1877 |
|
1369.1.1
by Aaron Bentley
Implement upgrade_charm, switch industrial_test to it. |
1878 |
def upgrade_charm(self, service, charm_path=None): |
1879 |
args = (service,) |
|
1880 |
if charm_path is not None: |
|
1881 |
args = args + ('--path', charm_path) |
|
1882 |
self.juju('upgrade-charm', args) |
|
1883 |
||
1221.4.1
by Aaron Bentley
Extract destroy-service to a method. |
1884 |
def remove_service(self, service): |
1420.2.1
by Aaron Bentley
Update tests to use applications, not services. |
1885 |
self.juju('remove-application', (service,)) |
1221.4.1
by Aaron Bentley
Extract destroy-service to a method. |
1886 |
|
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1887 |
@classmethod
|
1888 |
def format_bundle(cls, bundle_template): |
|
1889 |
return bundle_template.format(container=cls.preferred_container()) |
|
1890 |
||
1891 |
def deploy_bundle(self, bundle_template, timeout=_DEFAULT_BUNDLE_TIMEOUT): |
|
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
1892 |
"""Deploy bundle using native juju 2.0 deploy command."""
|
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1893 |
bundle = self.format_bundle(bundle_template) |
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
1894 |
self.juju('deploy', bundle, timeout=timeout) |
1895 |
||
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1896 |
def deployer(self, bundle_template, name=None, deploy_delay=10, |
1897 |
timeout=3600): |
|
1460.1.2
by Aaron Bentley
Updates from review |
1898 |
"""Deploy a bundle using deployer."""
|
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1899 |
bundle = self.format_bundle(bundle_template) |
884
by John George
Add juju-deployer test support. |
1900 |
args = ( |
1901 |
'--debug', |
|
1204.1.2
by John George
Pass deployer timeout values as function parameters. |
1902 |
'--deploy-delay', str(deploy_delay), |
1903 |
'--timeout', str(timeout), |
|
884
by John George
Add juju-deployer test support. |
1904 |
'--config', bundle, |
1905 |
)
|
|
953.2.1
by John George
Support taking a bundle name in addition to the bundle config file path. |
1906 |
if name: |
1907 |
args += (name,) |
|
1460.1.1
by Aaron Bentley
Remove local. prefix from deployer after beta-8 |
1908 |
e_arg = ('-e', '{}:{}'.format( |
1363.1.1
by Seman
Updated Deployer to support Juju 2.X. |
1909 |
self.env.controller.name, self.env.environment)) |
1910 |
args = e_arg + args |
|
1911 |
self.juju('deployer', args, self.env.needs_sudo(), include_e=False) |
|
884
by John George
Add juju-deployer test support. |
1912 |
|
1680.1.1
by Martin Packman
Add environment variable to disable space constraint on maas |
1913 |
@staticmethod
|
1914 |
def _maas_spaces_enabled(): |
|
1915 |
return not os.environ.get("JUJU_CI_SPACELESSNESS") |
|
1916 |
||
1260.2.30
by Aaron Bentley
Updates from review |
1917 |
def _get_substrate_constraints(self): |
1513.2.1
by Martin Packman
Remove arch=amd64 constraint for maas deploys |
1918 |
if self.env.joyent: |
1260.2.30
by Aaron Bentley
Updates from review |
1919 |
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
|
1920 |
return 'mem=2G cpu-cores=1' |
|
1680.1.1
by Martin Packman
Add environment variable to disable space constraint on maas |
1921 |
elif self.env.maas and self._maas_spaces_enabled(): |
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1922 |
# For now only maas support spaces in a meaningful way.
|
1675.1.1
by Martin Packman
Fix test failures, lint, and bundle issue from endpoint bindings |
1923 |
return 'mem=2G spaces={}'.format(','.join( |
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
1924 |
'^' + space for space in sorted(self.excluded_spaces))) |
1260.2.30
by Aaron Bentley
Updates from review |
1925 |
else: |
1926 |
return 'mem=2G' |
|
1927 |
||
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1928 |
def quickstart(self, bundle_template, upload_tools=False): |
796.2.2
by John George
Call quickstart from EnvJujuClient, with constraints |
1929 |
"""quickstart, using sudo if necessary."""
|
1341.2.5
by Aaron Bentley
Allow {container} expansion in bundle names. |
1930 |
bundle = self.format_bundle(bundle_template) |
1513.2.1
by Martin Packman
Remove arch=amd64 constraint for maas deploys |
1931 |
constraints = 'mem=2G' |
796.2.2
by John George
Call quickstart from EnvJujuClient, with constraints |
1932 |
args = ('--constraints', constraints) |
1933 |
if upload_tools: |
|
1934 |
args = ('--upload-tools',) + args |
|
1935 |
args = args + ('--no-browser', bundle,) |
|
966.2.2
by Curtis Hovey
pass extra_env={'JUJU': None} with the quickstart command. |
1936 |
self.juju('quickstart', args, self.env.needs_sudo(), |
1937 |
extra_env={'JUJU': self.full_path}) |
|
796.2.2
by John George
Call quickstart from EnvJujuClient, with constraints |
1938 |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1939 |
def status_until(self, timeout, start=None): |
874.2.8
by Aaron Bentley
Add docs, simplify get_unit. |
1940 |
"""Call and yield status until the timeout is reached.
|
1941 |
||
1942 |
Status will always be yielded once before checking the timeout.
|
|
1943 |
||
1944 |
This is intended for implementing things like wait_for_started.
|
|
1945 |
||
1946 |
:param timeout: The number of seconds to wait before timing out.
|
|
1947 |
:param start: If supplied, the time to count from when determining
|
|
1948 |
timeout.
|
|
1949 |
"""
|
|
1575.1.1
by Aaron Bentley
Soft timeouts for status_until. |
1950 |
with self.check_timeouts(): |
1951 |
with self.ignore_soft_deadline(): |
|
1952 |
yield self.get_status() |
|
1953 |
for remaining in until_timeout(timeout, start=start): |
|
1954 |
yield self.get_status() |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1955 |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
1956 |
def _wait_for_status(self, reporter, translate, exc_type=StatusNotMet, |
1196.1.4
by Martin Packman
Finish increased sanity on deployer tests |
1957 |
timeout=1200, start=None): |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1958 |
"""Wait till status reaches an expected state with pretty reporting.
|
1959 |
||
1960 |
Always tries to get status at least once. Each status call has an
|
|
1961 |
internal timeout of 60 seconds. This is independent of the timeout for
|
|
1962 |
the whole wait, note this means this function may be overrun.
|
|
1963 |
||
1964 |
:param reporter: A GroupReporter instance for output.
|
|
1965 |
:param translate: A callable that takes status to make states dict.
|
|
1196.1.6
by Martin Packman
Changes for review by abentley |
1966 |
:param exc_type: Optional StatusNotMet subclass to raise on timeout.
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1967 |
:param timeout: Optional number of seconds to wait before timing out.
|
1968 |
:param start: Optional time to count from when determining timeout.
|
|
1969 |
"""
|
|
1970 |
status = None |
|
1971 |
try: |
|
1564.3.7
by Aaron Bentley
Ensure check_timeouts is called after _wait_for_status. |
1972 |
with self.check_timeouts(): |
1973 |
with self.ignore_soft_deadline(): |
|
1974 |
for _ in chain([None], |
|
1975 |
until_timeout(timeout, start=start)): |
|
1976 |
try: |
|
1977 |
status = self.get_status() |
|
1978 |
except CannotConnectEnv: |
|
1979 |
log.info( |
|
1980 |
'Suppressing "Unable to connect to'
|
|
1981 |
' environment"') |
|
1982 |
continue
|
|
1983 |
states = translate(status) |
|
1984 |
if states is None: |
|
1985 |
break
|
|
1726.4.1
by Andrew Beach
Tried actually using check_for_errors which highlighted some errors. I had to change a bunch of existing errors just to get meaningful errors out of them. |
1986 |
status.raise_highest_error(ignore_recoverable=True) |
1564.3.7
by Aaron Bentley
Ensure check_timeouts is called after _wait_for_status. |
1987 |
reporter.update(states) |
1988 |
else: |
|
1989 |
if status is not None: |
|
1990 |
log.error(status.status_text) |
|
1726.4.1
by Andrew Beach
Tried actually using check_for_errors which highlighted some errors. I had to change a bunch of existing errors just to get meaningful errors out of them. |
1991 |
status.raise_highest_error( |
1992 |
ignore_recoverable=False) |
|
1564.3.7
by Aaron Bentley
Ensure check_timeouts is called after _wait_for_status. |
1993 |
raise exc_type(self.env.environment, status) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1994 |
finally: |
1995 |
reporter.finish() |
|
1996 |
return status |
|
1997 |
||
751.1.1
by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt |
1998 |
def wait_for_started(self, timeout=1200, start=None): |
657.1.5
by Aaron Bentley
Move wait_for_started to EnvJujuClient. |
1999 |
"""Wait until all unit/machine agents are 'started'."""
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2000 |
reporter = GroupReporter(sys.stdout, 'started') |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2001 |
return self._wait_for_status( |
2002 |
reporter, Status.check_agents_started, AgentsNotStarted, |
|
2003 |
timeout=timeout, start=start) |
|
657.1.5
by Aaron Bentley
Move wait_for_started to EnvJujuClient. |
2004 |
|
966.1.10
by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units. |
2005 |
def wait_for_subordinate_units(self, service, unit_prefix, timeout=1200, |
2006 |
start=None): |
|
966.1.12
by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter. |
2007 |
"""Wait until all service units have a started subordinate with
|
966.1.10
by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units. |
2008 |
unit_prefix."""
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2009 |
def status_to_subordinate_states(status): |
2010 |
service_unit_count = status.get_service_unit_count(service) |
|
2011 |
subordinate_unit_count = 0 |
|
2012 |
unit_states = defaultdict(list) |
|
2013 |
for name, unit in status.service_subordinate_units(service): |
|
2014 |
if name.startswith(unit_prefix + '/'): |
|
2015 |
subordinate_unit_count += 1 |
|
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
2016 |
unit_states[coalesce_agent_status(unit)].append(name) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2017 |
if (subordinate_unit_count == service_unit_count and |
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
2018 |
set(unit_states.keys()).issubset(AGENTS_READY)): |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2019 |
return None |
2020 |
return unit_states |
|
966.1.12
by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter. |
2021 |
reporter = GroupReporter(sys.stdout, 'started') |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2022 |
self._wait_for_status( |
2023 |
reporter, status_to_subordinate_states, AgentsNotStarted, |
|
2024 |
timeout=timeout, start=start) |
|
966.1.9
by John George
Add wait_for_subordinate_unit to jujupy.py. |
2025 |
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2026 |
def wait_for_version(self, version, timeout=300, start=None): |
2027 |
def status_to_version(status): |
|
2028 |
versions = status.get_agent_versions() |
|
2029 |
if versions.keys() == [version]: |
|
2030 |
return None |
|
2031 |
return versions |
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2032 |
reporter = GroupReporter(sys.stdout, version) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2033 |
self._wait_for_status(reporter, status_to_version, VersionsNotUpdated, |
1196.1.4
by Martin Packman
Finish increased sanity on deployer tests |
2034 |
timeout=timeout, start=start) |
657.1.6
by Aaron Bentley
Move wait_for_version to EnvJujuClient. |
2035 |
|
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2036 |
def list_models(self): |
2037 |
"""List the models registered with the current controller."""
|
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
2038 |
self.controller_juju('list-models', ()) |
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2039 |
|
2040 |
def get_models(self): |
|
1593.2.9
by Curtis Hovey
Require get_model() to be fast. |
2041 |
"""return a models dict with a 'models': [] key-value pair.
|
2042 |
||
2043 |
The server has 120 seconds to respond because this method is called
|
|
2044 |
often when tearing down a controller-less deployment.
|
|
2045 |
"""
|
|
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2046 |
output = self.get_juju_output( |
1427.1.1
by Christopher Lee
Update commands to use <controller>:<model> format. Fix arg to -c to be controller name. |
2047 |
'list-models', '-c', self.env.controller.name, '--format', 'yaml', |
1593.2.9
by Curtis Hovey
Require get_model() to be fast. |
2048 |
include_e=False, timeout=120) |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
2049 |
models = yaml.safe_load(output) |
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2050 |
return models |
2051 |
||
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
2052 |
def _get_models(self): |
2053 |
"""return a list of model dicts."""
|
|
2054 |
return self.get_models()['models'] |
|
2055 |
||
2056 |
def iter_model_clients(self): |
|
1318.2.1
by Aaron Bentley
Unify cloning code, add docs. |
2057 |
"""Iterate through all the models that share this model's controller.
|
2058 |
||
2059 |
Works only if JES is enabled.
|
|
2060 |
"""
|
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
2061 |
models = self._get_models() |
2062 |
if not models: |
|
2063 |
yield self |
|
1612.2.16
by Christopher Lee
Remove bad changes to iter_client_models, Remove unneeded test. Fix existing test. |
2064 |
for model in models: |
2065 |
yield self._acquire_model_client(model['name']) |
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
2066 |
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
2067 |
def get_controller_model_name(self): |
2068 |
"""Return the name of the 'controller' model.
|
|
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2069 |
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
2070 |
Return the name of the environment when an 'controller' model does
|
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2071 |
not exist.
|
2072 |
"""
|
|
1423.1.1
by Curtis Hovey
Rename 'admin' model to 'controller', preserve old name in EnvJujuClient2B7. |
2073 |
return 'controller' |
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
2074 |
|
1318.2.1
by Aaron Bentley
Unify cloning code, add docs. |
2075 |
def _acquire_model_client(self, name): |
2076 |
"""Get a client for a model with the supplied name.
|
|
2077 |
||
2078 |
If the name matches self, self is used. Otherwise, a clone is used.
|
|
2079 |
"""
|
|
2080 |
if name == self.env.environment: |
|
2081 |
return self |
|
2082 |
else: |
|
2083 |
env = self.env.clone(model_name=name) |
|
2084 |
return self.clone(env=env) |
|
2085 |
||
1491.2.20
by Christopher Lee
Add test for hosted models. |
2086 |
def get_model_uuid(self): |
2087 |
name = self.env.environment |
|
1556.1.2
by Curtis Hovey
Always pass the controller:model to show-model when getting uuid. |
2088 |
model = self._cmd_model(True, False) |
1556.1.1
by Curtis Hovey
Don't pass -m to show-model. |
2089 |
output_yaml = self.get_juju_output( |
1556.1.2
by Curtis Hovey
Always pass the controller:model to show-model when getting uuid. |
2090 |
'show-model', '--format', 'yaml', model, include_e=False) |
1491.2.20
by Christopher Lee
Add test for hosted models. |
2091 |
output = yaml.safe_load(output_yaml) |
2092 |
return output[name]['model-uuid'] |
|
2093 |
||
1491.2.14
by Christopher Lee
Make test pass for added get_controller_uuid |
2094 |
def get_controller_uuid(self): |
2095 |
name = self.env.controller.name |
|
2096 |
output_yaml = self.get_juju_output( |
|
2097 |
'show-controller', '--format', 'yaml', include_e=False) |
|
2098 |
output = yaml.safe_load(output_yaml) |
|
2099 |
return output[name]['details']['uuid'] |
|
2100 |
||
1576.1.1
by Christopher Lee
Add, test and use get_controller_model_uuid method. |
2101 |
def get_controller_model_uuid(self): |
2102 |
output_yaml = self.get_juju_output( |
|
2103 |
'show-model', 'controller', '--format', 'yaml', include_e=False) |
|
2104 |
output = yaml.safe_load(output_yaml) |
|
2105 |
return output['controller']['model-uuid'] |
|
2106 |
||
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
2107 |
def get_controller_client(self): |
2108 |
"""Return a client for the controller model. May return self.
|
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
2109 |
|
1363.4.4
by Aaron Bentley
Rename create_model to add_model. |
2110 |
This may be inaccurate for models created using add_model
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
2111 |
rather than bootstrap.
|
2112 |
"""
|
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
2113 |
return self._acquire_model_client(self.get_controller_model_name()) |
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
2114 |
|
2115 |
def list_controllers(self): |
|
2116 |
"""List the controllers."""
|
|
2117 |
self.juju('list-controllers', (), include_e=False) |
|
2118 |
||
1306.1.4
by Curtis Hovey
Added get_controller_endpoint for show-controller. |
2119 |
def get_controller_endpoint(self): |
2120 |
"""Return the address of the controller leader."""
|
|
1324.1.1
by Aaron Bentley
Use controller name for show-controller. |
2121 |
controller = self.env.controller.name |
1306.1.19
by Curtis Hovey
Fix calls to show-controller with include_e=False. |
2122 |
output = self.get_juju_output( |
1324.1.1
by Aaron Bentley
Use controller name for show-controller. |
2123 |
'show-controller', controller, include_e=False) |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
2124 |
info = yaml.safe_load(output) |
1324.1.1
by Aaron Bentley
Use controller name for show-controller. |
2125 |
endpoint = info[controller]['details']['api-endpoints'][0] |
1310.1.2
by Curtis Hovey
Address ipv6 split address and port. |
2126 |
address, port = split_address_port(endpoint) |
1306.1.4
by Curtis Hovey
Added get_controller_endpoint for show-controller. |
2127 |
return address |
2128 |
||
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2129 |
def get_controller_members(self): |
1306.1.8
by Curtis Hovey
Update docstrings. |
2130 |
"""Return a list of Machines that are members of the controller.
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2131 |
|
1306.1.8
by Curtis Hovey
Update docstrings. |
2132 |
The first machine in the list is the leader. the remaining machines
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2133 |
are followers in a HA relationship.
|
2134 |
"""
|
|
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
2135 |
members = [] |
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2136 |
status = self.get_status() |
1315.2.1
by Aaron Bentley
Use machine id instead of machine number. |
2137 |
for machine_id, machine in status.iter_machines(): |
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2138 |
if self.get_controller_member_status(machine): |
1315.2.1
by Aaron Bentley
Use machine id instead of machine number. |
2139 |
members.append(Machine(machine_id, machine)) |
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
2140 |
if len(members) <= 1: |
2141 |
return members |
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2142 |
# Search for the leader and make it the first in the list.
|
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
2143 |
# If the endpoint address is not the same as the leader's dns_name,
|
2144 |
# the members are return in the order they were discovered.
|
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2145 |
endpoint = self.get_controller_endpoint() |
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
2146 |
log.debug('Controller endpoint is at {}'.format(endpoint)) |
2147 |
members.sort(key=lambda m: m.info.get('dns-name') != endpoint) |
|
2148 |
return members |
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
2149 |
|
1306.1.7
by Curtis Hovey
Added get_controller_leader. |
2150 |
def get_controller_leader(self): |
1306.1.8
by Curtis Hovey
Update docstrings. |
2151 |
"""Return the controller leader Machine."""
|
1306.1.7
by Curtis Hovey
Added get_controller_leader. |
2152 |
controller_members = self.get_controller_members() |
2153 |
return controller_members[0] |
|
2154 |
||
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
2155 |
@staticmethod
|
2156 |
def get_controller_member_status(info_dict): |
|
1306.1.8
by Curtis Hovey
Update docstrings. |
2157 |
"""Return the controller-member-status of the machine if it exists."""
|
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
2158 |
return info_dict.get('controller-member-status') |
2159 |
||
703.1.1
by Aaron Bentley
Add ensure-availability stage to industrial_test. |
2160 |
def wait_for_ha(self, timeout=1200): |
2161 |
desired_state = 'has-vote' |
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2162 |
reporter = GroupReporter(sys.stdout, desired_state) |
943.1.1
by Martin Packman
Use try/finally to always call finish on GroupReporter instances |
2163 |
try: |
1564.3.8
by Aaron Bentley
Handle soft deadline in wait_for_ha. |
2164 |
with self.check_timeouts(): |
2165 |
with self.ignore_soft_deadline(): |
|
1738.2.3
by Andrew Beach
Minor adjustment. |
2166 |
status = None |
1564.3.8
by Aaron Bentley
Handle soft deadline in wait_for_ha. |
2167 |
for remaining in until_timeout(timeout): |
2168 |
status = self.get_status(controller=True) |
|
2169 |
status.check_agents_started() |
|
2170 |
states = {} |
|
2171 |
for machine, info in status.iter_machines(): |
|
2172 |
status = self.get_controller_member_status(info) |
|
2173 |
if status is None: |
|
2174 |
continue
|
|
2175 |
states.setdefault(status, []).append(machine) |
|
2176 |
if states.keys() == [desired_state]: |
|
2177 |
if len(states.get(desired_state, [])) >= 3: |
|
2178 |
break
|
|
2179 |
reporter.update(states) |
|
2180 |
else: |
|
1738.2.1
by Andrew Beach
Added StatusNotMet exceptions for wait_for_{deploy_started,ha}. |
2181 |
raise VotingNotEnabled(self.env.environment, status) |
943.1.1
by Martin Packman
Use try/finally to always call finish on GroupReporter instances |
2182 |
finally: |
889.2.1
by Martin Packman
Make sure to finish GroupReporter instances even in error cases |
2183 |
reporter.finish() |
1449.2.4
by Curtis Hovey
Break from the look in wait_for_ha so that reporter.finish is always called before pause. |
2184 |
# XXX sinzui 2014-12-04: bug 1399277 happens because
|
2185 |
# juju claims HA is ready when the monogo replica sets
|
|
2186 |
# are not. Juju is not fully usable. The replica set
|
|
2187 |
# lag might be 5 minutes.
|
|
2188 |
self._backend.pause(300) |
|
703.1.1
by Aaron Bentley
Add ensure-availability stage to industrial_test. |
2189 |
|
796.2.1
by John George
Add quickstart_deploy.py |
2190 |
def wait_for_deploy_started(self, service_count=1, timeout=1200): |
2191 |
"""Wait until service_count services are 'started'.
|
|
2192 |
||
2193 |
:param service_count: The number of services for which to wait.
|
|
2194 |
:param timeout: The number of seconds to wait.
|
|
2195 |
"""
|
|
1564.3.9
by Aaron Bentley
Handle soft timeouts in wait_for_deploy_started. |
2196 |
with self.check_timeouts(): |
2197 |
with self.ignore_soft_deadline(): |
|
1738.2.1
by Andrew Beach
Added StatusNotMet exceptions for wait_for_{deploy_started,ha}. |
2198 |
status = None |
1564.3.9
by Aaron Bentley
Handle soft timeouts in wait_for_deploy_started. |
2199 |
for remaining in until_timeout(timeout): |
2200 |
status = self.get_status() |
|
2201 |
if status.get_service_count() >= service_count: |
|
2202 |
return
|
|
2203 |
else: |
|
1738.2.1
by Andrew Beach
Added StatusNotMet exceptions for wait_for_{deploy_started,ha}. |
2204 |
raise ApplicationsNotStarted(self.env.environment, status) |
796.2.1
by John George
Add quickstart_deploy.py |
2205 |
|
1215
by John George
Workload timeout increate to 10 minutes. |
2206 |
def wait_for_workloads(self, timeout=600, start=None): |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2207 |
"""Wait until all unit workloads are in a ready state."""
|
2208 |
def status_to_workloads(status): |
|
2209 |
unit_states = defaultdict(list) |
|
2210 |
for name, unit in status.iter_units(): |
|
2211 |
workload = unit.get('workload-status') |
|
2212 |
if workload is not None: |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
2213 |
state = workload['current'] |
1204.3.1
by Martin Packman
Fix bug introduced with unknown workloads and add test coverage |
2214 |
else: |
2215 |
state = 'unknown' |
|
2216 |
unit_states[state].append(name) |
|
2217 |
if set(('active', 'unknown')).issuperset(unit_states): |
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2218 |
return None |
1204.3.1
by Martin Packman
Fix bug introduced with unknown workloads and add test coverage |
2219 |
unit_states.pop('unknown', None) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2220 |
return unit_states |
1196.1.6
by Martin Packman
Changes for review by abentley |
2221 |
reporter = GroupReporter(sys.stdout, 'active') |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2222 |
self._wait_for_status(reporter, status_to_workloads, WorkloadsNotReady, |
1196.1.4
by Martin Packman
Finish increased sanity on deployer tests |
2223 |
timeout=timeout, start=start) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
2224 |
|
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
2225 |
def wait_for(self, thing, search_type, timeout=300): |
1091.3.2
by James Tunnicliffe
clean_environment bails early if there is no environment to clean. |
2226 |
""" Wait for a something (thing) matching none/all/some machines.
|
2227 |
||
2228 |
Examples:
|
|
2229 |
wait_for('containers', 'all')
|
|
2230 |
This will wait for a container to appear on all machines.
|
|
2231 |
||
2232 |
wait_for('machines-not-0', 'none')
|
|
2233 |
This will wait for all machines other than 0 to be removed.
|
|
2234 |
||
2235 |
:param thing: string, either 'containers' or 'not-machine-0'
|
|
2236 |
:param search_type: string containing none, some or all
|
|
2237 |
:param timeout: number of seconds to wait for condition to be true.
|
|
2238 |
:return:
|
|
2239 |
"""
|
|
2240 |
try: |
|
2241 |
for status in self.status_until(timeout): |
|
2242 |
hit = False |
|
2243 |
miss = False |
|
2244 |
||
2245 |
for machine, details in status.status['machines'].iteritems(): |
|
2246 |
if thing == 'containers': |
|
2247 |
if 'containers' in details: |
|
2248 |
hit = True |
|
2249 |
else: |
|
2250 |
miss = True |
|
2251 |
||
2252 |
elif thing == 'machines-not-0': |
|
2253 |
if machine != '0': |
|
2254 |
hit = True |
|
2255 |
else: |
|
2256 |
miss = True |
|
2257 |
||
2258 |
else: |
|
2259 |
raise ValueError("Unrecognised thing to wait for: %s", |
|
2260 |
thing) |
|
2261 |
||
2262 |
if search_type == 'none': |
|
2263 |
if not hit: |
|
2264 |
return
|
|
2265 |
elif search_type == 'some': |
|
2266 |
if hit: |
|
2267 |
return
|
|
2268 |
elif search_type == 'all': |
|
2269 |
if not miss: |
|
2270 |
return
|
|
2271 |
except Exception: |
|
2272 |
raise Exception("Timed out waiting for %s" % thing) |
|
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
2273 |
|
657.1.4
by Aaron Bentley
Move upgrade functionality to EnvJujuClient. |
2274 |
def get_matching_agent_version(self, no_build=False): |
2275 |
# strip the series and srch from the built version.
|
|
2276 |
version_parts = self.version.split('-') |
|
2277 |
if len(version_parts) == 4: |
|
2278 |
version_number = '-'.join(version_parts[0:2]) |
|
2279 |
else: |
|
2280 |
version_number = version_parts[0] |
|
2281 |
if not no_build and self.env.local: |
|
2282 |
version_number += '.1' |
|
2283 |
return version_number |
|
2284 |
||
1540.3.3
by Christopher Lee
Separate out controller update and model. Only update 'self' model not all models under controller |
2285 |
def upgrade_juju(self, force_version=True): |
2286 |
args = () |
|
2287 |
if force_version: |
|
2288 |
version = self.get_matching_agent_version(no_build=True) |
|
1612.2.12
by Christopher Lee
Update version argument as per recent change in juju. |
2289 |
args += ('--agent-version', version) |
1540.3.3
by Christopher Lee
Separate out controller update and model. Only update 'self' model not all models under controller |
2290 |
self._upgrade_juju(args) |
2291 |
||
2292 |
def _upgrade_juju(self, args): |
|
2293 |
self.juju('upgrade-juju', args) |
|
2294 |
||
1258.2.5
by Curtis Hovey
Added upgrade_mongo to EnvJujuClient. |
2295 |
def upgrade_mongo(self): |
2296 |
self.juju('upgrade-mongo', ()) |
|
2297 |
||
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
2298 |
def backup(self): |
808.2.6
by Aaron Bentley
Fix Azure terminate instances to work as Juju expects. |
2299 |
try: |
1419.1.21
by Aaron Bentley
Use get_juju_output for backup. |
2300 |
output = self.get_juju_output('create-backup') |
808.2.6
by Aaron Bentley
Fix Azure terminate instances to work as Juju expects. |
2301 |
except subprocess.CalledProcessError as e: |
1091.4.1
by James Tunnicliffe
Merged upstream |
2302 |
log.info(e.output) |
808.2.6
by Aaron Bentley
Fix Azure terminate instances to work as Juju expects. |
2303 |
raise
|
1091.4.1
by James Tunnicliffe
Merged upstream |
2304 |
log.info(output) |
757.1.1
by Curtis Hovey
Support both .tgz and .tar.gz backup file names. |
2305 |
backup_file_pattern = re.compile('(juju-backup-[0-9-]+\.(t|tar.)gz)') |
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
2306 |
match = backup_file_pattern.search(output) |
2307 |
if match is None: |
|
2308 |
raise Exception("The backup file was not found in output: %s" % |
|
2309 |
output) |
|
2310 |
backup_file_name = match.group(1) |
|
2311 |
backup_file_path = os.path.abspath(backup_file_name) |
|
1091.4.1
by James Tunnicliffe
Merged upstream |
2312 |
log.info("State-Server backup at %s", backup_file_path) |
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
2313 |
return backup_file_path |
2314 |
||
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
2315 |
def restore_backup(self, backup_file): |
1593.2.5
by Curtis Hovey
Fix tests. |
2316 |
self.juju( |
1593.2.1
by Curtis Hovey
Show the restore output as it happens; do not call status if None was returned. |
2317 |
'restore-backup', |
2318 |
('-b', '--constraints', 'mem=2G', '--file', backup_file)) |
|
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
2319 |
|
2320 |
def restore_backup_async(self, backup_file): |
|
1281.1.1
by Aaron Bentley
Rollback r1280 per Ian's email. |
2321 |
return self.juju_async('restore-backup', ('-b', '--constraints', |
2322 |
'mem=2G', '--file', backup_file)) |
|
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
2323 |
|
2324 |
def enable_ha(self): |
|
1634.1.1
by Curtis Hovey
enable-ha now takes -c or nothing. not -m. |
2325 |
self.juju( |
1634.1.2
by Curtis Hovey
Added guard for controller name. |
2326 |
'enable-ha', ('-n', '3', '-c', self.env.controller.name), |
1634.1.1
by Curtis Hovey
enable-ha now takes -c or nothing. not -m. |
2327 |
include_e=False) |
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
2328 |
|
953.3.11
by Nate Finch
set longer timeout for fill log actions, and fix -e testing problem |
2329 |
def action_fetch(self, id, action=None, timeout="1m"): |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2330 |
"""Fetches the results of the action with the given id.
|
2331 |
||
953.3.5
by Nate Finch
tests for new action stuff |
2332 |
Will wait for up to 1 minute for the action results.
|
953.3.7
by Nate Finch
update for code review comments |
2333 |
The action name here is just used for an more informational error in
|
953.3.5
by Nate Finch
tests for new action stuff |
2334 |
cases where it's available.
|
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2335 |
Returns the yaml output of the fetched action.
|
2336 |
"""
|
|
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
2337 |
out = self.get_juju_output("show-action-output", id, "--wait", timeout) |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
2338 |
status = yaml.safe_load(out)["status"] |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2339 |
if status != "completed": |
953.3.8
by Nate Finch
more review changes |
2340 |
name = "" |
953.3.7
by Nate Finch
update for code review comments |
2341 |
if action is not None: |
953.3.8
by Nate Finch
more review changes |
2342 |
name = " " + action |
953.3.9
by Nate Finch
more code review changes |
2343 |
raise Exception( |
2344 |
"timed out waiting for action%s to complete during fetch" % |
|
2345 |
name) |
|
953.3.2
by Nate Finch
log_rot fixes |
2346 |
return out |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2347 |
|
2348 |
def action_do(self, unit, action, *args): |
|
2349 |
"""Performs the given action on the given unit.
|
|
2350 |
||
2351 |
Action params should be given as args in the form foo=bar.
|
|
2352 |
Returns the id of the queued action.
|
|
2353 |
"""
|
|
953.3.13
by Nate Finch
more code review changes |
2354 |
args = (unit, action) + args |
2355 |
||
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
2356 |
output = self.get_juju_output("run-action", *args) |
953.3.9
by Nate Finch
more code review changes |
2357 |
action_id_pattern = re.compile( |
2358 |
'Action queued with id: ([a-f0-9\-]{36})') |
|
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2359 |
match = action_id_pattern.search(output) |
2360 |
if match is None: |
|
2361 |
raise Exception("Action id not found in output: %s" % |
|
2362 |
output) |
|
2363 |
return match.group(1) |
|
2364 |
||
953.3.11
by Nate Finch
set longer timeout for fill log actions, and fix -e testing problem |
2365 |
def action_do_fetch(self, unit, action, timeout="1m", *args): |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
2366 |
"""Performs given action on given unit and waits for the results.
|
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2367 |
|
2368 |
Action params should be given as args in the form foo=bar.
|
|
2369 |
Returns the yaml output of the action.
|
|
2370 |
"""
|
|
2371 |
id = self.action_do(unit, action, *args) |
|
953.3.11
by Nate Finch
set longer timeout for fill log actions, and fix -e testing problem |
2372 |
return self.action_fetch(id, action, timeout) |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
2373 |
|
1631.4.9
by Andrew Beach
Adjusted the check in assess_to to use hostname rather than dns name. (Might have to use both.) Updated EnvJujuClient.run to accomidate. |
2374 |
def run(self, commands, applications=None, machines=None, use_json=True): |
2375 |
args = [] |
|
2376 |
if use_json: |
|
2377 |
args.extend(['--format', 'json']) |
|
2378 |
if applications is not None: |
|
2379 |
args.extend(['--application', ','.join(applications)]) |
|
2380 |
if machines is not None: |
|
2381 |
args.extend(['--machine', ','.join(machines)]) |
|
2382 |
args.extend(commands) |
|
2383 |
responces = self.get_juju_output('run', *args) |
|
2384 |
if use_json: |
|
2385 |
return json.loads(responces) |
|
2386 |
else: |
|
2387 |
return responces |
|
1449.1.3
by Aaron Bentley
Implement EnvJujuClient.run. |
2388 |
|
1221.5.1
by Aaron Bentley
Extract EnvJujuClient.list_space. |
2389 |
def list_space(self): |
1221.5.2
by Aaron Bentley
Support list-space for EnvJujuClient. |
2390 |
return yaml.safe_load(self.get_juju_output('list-space')) |
1221.5.1
by Aaron Bentley
Extract EnvJujuClient.list_space. |
2391 |
|
1221.5.3
by Aaron Bentley
Extract EnvJujuClient.add_space. |
2392 |
def add_space(self, space): |
1221.5.4
by Aaron Bentley
Support space create => add-space. |
2393 |
self.juju('add-space', (space),) |
1221.5.3
by Aaron Bentley
Extract EnvJujuClient.add_space. |
2394 |
|
1221.5.5
by Aaron Bentley
Extract add_subnet. |
2395 |
def add_subnet(self, subnet, space): |
1221.5.6
by Aaron Bentley
Rename subnet add to add-subnet. |
2396 |
self.juju('add-subnet', (subnet, space)) |
1221.5.5
by Aaron Bentley
Extract add_subnet. |
2397 |
|
1369.2.1
by Seman
Update assess_multi_series_charms to support Juju 1.X tests. |
2398 |
def is_juju1x(self): |
2399 |
return self.version.startswith('1.') |
|
2400 |
||
1344.3.21
by Nicholas Skaggs
refactored testcase |
2401 |
def _get_register_command(self, output): |
1419.5.4
by Christopher Lee
Fix tests and add another. Change what add_user returns |
2402 |
"""Return register token from add-user output.
|
2403 |
||
2404 |
Return the register token supplied within the output from the add-user
|
|
2405 |
command.
|
|
2406 |
||
2407 |
"""
|
|
1344.3.19
by Nicholas Skaggs
initial refactoring |
2408 |
for row in output.split('\n'): |
2409 |
if 'juju register' in row: |
|
1419.5.4
by Christopher Lee
Fix tests and add another. Change what add_user returns |
2410 |
command_string = row.strip().lstrip() |
2411 |
command_parts = command_string.split(' ') |
|
2412 |
return command_parts[-1] |
|
1344.3.19
by Nicholas Skaggs
initial refactoring |
2413 |
raise AssertionError('Juju register command not found in output') |
2414 |
||
1562.2.3
by Aaron Bentley
Extract add_user, rename existing to add_user_perms. |
2415 |
def add_user(self, username): |
1427.3.17
by Christopher Lee
Revamp user test with chanegs to jujupy too. |
2416 |
"""Adds provided user and return register command arguments.
|
2417 |
||
2418 |
:return: Registration token provided by the add-user command.
|
|
2419 |
"""
|
|
1562.2.1
by Aaron Bentley
Support new model-less format. |
2420 |
output = self.get_juju_output( |
2421 |
'add-user', username, '-c', self.env.controller.name, |
|
2422 |
include_e=False) |
|
1562.2.3
by Aaron Bentley
Extract add_user, rename existing to add_user_perms. |
2423 |
return self._get_register_command(output) |
2424 |
||
2425 |
def add_user_perms(self, username, models=None, permissions='login'): |
|
2426 |
"""Adds provided user and return register command arguments.
|
|
2427 |
||
2428 |
:return: Registration token provided by the add-user command.
|
|
2429 |
"""
|
|
2430 |
output = self.add_user(username) |
|
1562.2.1
by Aaron Bentley
Support new model-less format. |
2431 |
self.grant(username, permissions, models) |
1562.2.3
by Aaron Bentley
Extract add_user, rename existing to add_user_perms. |
2432 |
return output |
1427.3.17
by Christopher Lee
Revamp user test with chanegs to jujupy too. |
2433 |
|
1423.1.1
by Curtis Hovey
Rename 'admin' model to 'controller', preserve old name in EnvJujuClient2B7. |
2434 |
def revoke(self, username, models=None, permissions='read'): |
1344.3.19
by Nicholas Skaggs
initial refactoring |
2435 |
if models is None: |
2436 |
models = self.env.environment |
|
1344.3.24
by Nicholas Skaggs
working test_jujupy |
2437 |
|
1541.2.5
by Curtis Hovey
Moved new functions into EnvJujuClient. |
2438 |
args = (username, permissions, models) |
1344.3.23
by Nicholas Skaggs
Testcase works again |
2439 |
|
1344.3.28
by Nicholas Skaggs
fixes per Aaron's comments |
2440 |
self.controller_juju('revoke', args) |
1344.3.19
by Nicholas Skaggs
initial refactoring |
2441 |
|
1466.2.1
by Leo Zhang
Fit version 1.x |
2442 |
def add_storage(self, unit, storage_type, amount="1"): |
2443 |
"""Add storage instances to service.
|
|
2444 |
||
2445 |
Only type 'disk' is able to add instances.
|
|
2446 |
"""
|
|
2447 |
self.juju('add-storage', (unit, storage_type + "=" + amount)) |
|
2448 |
||
2449 |
def list_storage(self): |
|
2450 |
"""Return the storage list."""
|
|
2451 |
return self.get_juju_output('list-storage', '--format', 'json') |
|
2452 |
||
2453 |
def list_storage_pool(self): |
|
2454 |
"""Return the list of storage pool."""
|
|
2455 |
return self.get_juju_output('list-storage-pools', '--format', 'json') |
|
2456 |
||
2457 |
def create_storage_pool(self, name, provider, size): |
|
2458 |
"""Create storage pool."""
|
|
2459 |
self.juju('create-storage-pool', |
|
2460 |
(name, provider, |
|
2461 |
'size={}'.format(size))) |
|
2462 |
||
1477.2.12
by Leo Zhang
Fake merge of trunk |
2463 |
def disable_user(self, user_name): |
2464 |
"""Disable an user"""
|
|
2465 |
self.controller_juju('disable-user', (user_name,)) |
|
2466 |
||
2467 |
def enable_user(self, user_name): |
|
2468 |
"""Enable an user"""
|
|
2469 |
self.controller_juju('enable-user', (user_name,)) |
|
2470 |
||
2471 |
def logout(self): |
|
2472 |
"""Logout an user"""
|
|
2473 |
self.controller_juju('logout', ()) |
|
1477.3.1
by Andrew Wilkins
beebop |
2474 |
self.env.user_name = '' |
1477.2.12
by Leo Zhang
Fake merge of trunk |
2475 |
|
1427.3.18
by Christopher Lee
Further additions re: controller names and usage. |
2476 |
def register_user(self, user, juju_home, controller_name=None): |
1477.2.12
by Leo Zhang
Fake merge of trunk |
2477 |
"""Register `user` for the `client` return the cloned client used."""
|
2478 |
username = user.name |
|
1427.3.18
by Christopher Lee
Further additions re: controller names and usage. |
2479 |
if controller_name is None: |
2480 |
controller_name = '{}_controller'.format(username) |
|
1477.2.12
by Leo Zhang
Fake merge of trunk |
2481 |
|
2482 |
model = self.env.environment |
|
1562.2.3
by Aaron Bentley
Extract add_user, rename existing to add_user_perms. |
2483 |
token = self.add_user_perms(username, models=model, |
2484 |
permissions=user.permissions) |
|
1477.2.12
by Leo Zhang
Fake merge of trunk |
2485 |
user_client = self.create_cloned_environment(juju_home, |
1477.3.1
by Andrew Wilkins
beebop |
2486 |
controller_name, |
2487 |
username) |
|
1477.2.12
by Leo Zhang
Fake merge of trunk |
2488 |
|
2489 |
try: |
|
1427.3.17
by Christopher Lee
Revamp user test with chanegs to jujupy too. |
2490 |
child = user_client.expect('register', (token), include_e=False) |
1631.1.1
by Curtis Hovey
Expect password before username when registering a user. |
2491 |
child.expect('(?i)password') |
2492 |
child.sendline(username + '_password') |
|
2493 |
child.expect('(?i)password') |
|
2494 |
child.sendline(username + '_password') |
|
1477.2.12
by Leo Zhang
Fake merge of trunk |
2495 |
child.expect('(?i)name') |
1427.3.18
by Christopher Lee
Further additions re: controller names and usage. |
2496 |
child.sendline(controller_name) |
1477.2.12
by Leo Zhang
Fake merge of trunk |
2497 |
child.expect(pexpect.EOF) |
2498 |
if child.isalive(): |
|
2499 |
raise Exception( |
|
2500 |
'Registering user failed: pexpect session still alive') |
|
2501 |
except pexpect.TIMEOUT: |
|
2502 |
raise Exception( |
|
2503 |
'Registering user failed: pexpect session timed out') |
|
1541.2.8
by Curtis Hovey
Remove Use set-model-config instrad of deploy. |
2504 |
user_client.env.user_name = username |
1477.2.12
by Leo Zhang
Fake merge of trunk |
2505 |
return user_client |
2506 |
||
1711.3.4
by Aaron Bentley
Implement register_host. |
2507 |
def register_host(self, host, email, password): |
2508 |
child = self.expect('register', ('--no-browser-login', host), |
|
2509 |
include_e=False) |
|
2510 |
try: |
|
2511 |
child.logfile = sys.stdout |
|
2512 |
child.expect('E-Mail:|Enter a name for this controller:') |
|
1721.1.1
by Aaron Bentley
child.match is a regex match, not a string. |
2513 |
if child.match.group(0) == 'E-Mail:': |
1711.3.4
by Aaron Bentley
Implement register_host. |
2514 |
child.sendline(email) |
2515 |
child.expect('Password:') |
|
2516 |
child.logfile = None |
|
2517 |
try: |
|
2518 |
child.sendline(password) |
|
2519 |
finally: |
|
2520 |
child.logfile = sys.stdout |
|
2521 |
child.expect(r'Two-factor auth \(Enter for none\):') |
|
2522 |
child.sendline() |
|
2523 |
child.expect('Enter a name for this controller:') |
|
2524 |
child.sendline(self.env.controller.name) |
|
2525 |
child.expect(pexpect.EOF) |
|
2526 |
except pexpect.TIMEOUT: |
|
2527 |
raise
|
|
2528 |
raise Exception( |
|
2529 |
'Registering user failed: pexpect session timed out') |
|
2530 |
||
1541.2.8
by Curtis Hovey
Remove Use set-model-config instrad of deploy. |
2531 |
def remove_user(self, username): |
2532 |
self.juju('remove-user', (username, '-y'), include_e=False) |
|
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
2533 |
|
2534 |
def create_cloned_environment( |
|
2535 |
self, cloned_juju_home, controller_name, user_name=None): |
|
2536 |
"""Create a cloned environment.
|
|
2537 |
||
2538 |
If `user_name` is passed ensures that the cloned environment is updated
|
|
2539 |
to match.
|
|
2540 |
||
2541 |
"""
|
|
2542 |
user_client = self.clone(env=self.env.clone()) |
|
2543 |
user_client.env.juju_home = cloned_juju_home |
|
2544 |
if user_name is not None and user_name != self.env.user_name: |
|
2545 |
user_client.env.user_name = user_name |
|
2546 |
user_client.env.environment = qualified_model_name( |
|
2547 |
user_client.env.environment, self.env.user_name) |
|
2548 |
# New user names the controller.
|
|
2549 |
user_client.env.controller = Controller(controller_name) |
|
2550 |
return user_client |
|
2551 |
||
2552 |
def grant(self, user_name, permission, model=None): |
|
2553 |
"""Grant the user with model or controller permission."""
|
|
2554 |
if permission in self.controller_permissions: |
|
1427.3.18
by Christopher Lee
Further additions re: controller names and usage. |
2555 |
self.juju( |
2556 |
'grant', |
|
2557 |
(user_name, permission, '-c', self.env.controller.name), |
|
2558 |
include_e=False) |
|
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
2559 |
elif permission in self.model_permissions: |
2560 |
if model is None: |
|
2561 |
model = self.model_name |
|
1427.3.17
by Christopher Lee
Revamp user test with chanegs to jujupy too. |
2562 |
self.juju( |
2563 |
'grant', |
|
2564 |
(user_name, permission, model, '-c', self.env.controller.name), |
|
2565 |
include_e=False) |
|
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
2566 |
else: |
1562.2.2
by Aaron Bentley
Test add_user grants. |
2567 |
raise ValueError('Unknown permission {}'.format(permission)) |
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
2568 |
|
1556.2.2
by Leo Zhang
changes after review |
2569 |
def list_clouds(self, format='json'): |
2570 |
"""List all the available clouds."""
|
|
2571 |
return self.get_juju_output('list-clouds', '--format', |
|
2572 |
format, include_e=False) |
|
2573 |
||
1727.3.3
by Aaron Bentley
Add cloud parameter to add_cloud_interactive. |
2574 |
def add_cloud_interactive(self, cloud_name, cloud): |
1711.6.2
by Aaron Bentley
Test interactive add-cloud against fake juju client. |
2575 |
child = self.expect('add-cloud', include_e=False) |
2576 |
try: |
|
2577 |
child.logfile = sys.stdout |
|
2578 |
child.expect('Select cloud type:') |
|
2579 |
child.sendline(cloud['type']) |
|
1727.2.17
by Aaron Bentley
Add bogus type testing. |
2580 |
child.expect('(Enter a name for the cloud:)|(Select cloud type:)') |
2581 |
if child.match.group(2) is not None: |
|
2582 |
raise TypeNotAccepted('Cloud type not accepted.') |
|
1711.6.2
by Aaron Bentley
Test interactive add-cloud against fake juju client. |
2583 |
child.sendline(cloud_name) |
2584 |
if cloud['type'] == 'maas': |
|
2585 |
child.expect('Enter the API endpoint url:') |
|
2586 |
child.sendline(cloud['endpoint']) |
|
2587 |
if cloud['type'] == 'manual': |
|
1727.2.18
by Aaron Bentley
Check invalid name handling. |
2588 |
child.expect( |
2589 |
"(Enter the controller's hostname or IP address:)|"
|
|
2590 |
"(Enter a name for the cloud:)") |
|
2591 |
if child.match.group(2) is not None: |
|
2592 |
raise NameNotAccepted('Cloud name not accepted.') |
|
1711.6.2
by Aaron Bentley
Test interactive add-cloud against fake juju client. |
2593 |
child.sendline(cloud['endpoint']) |
2594 |
if cloud['type'] == 'openstack': |
|
2595 |
child.expect('Enter the API endpoint url for the cloud:') |
|
2596 |
child.sendline(cloud['endpoint']) |
|
2597 |
child.expect( |
|
2598 |
'Select one or more auth types separated by commas:') |
|
2599 |
child.sendline(','.join(cloud['auth-types'])) |
|
2600 |
for num, (name, values) in enumerate(cloud['regions'].items()): |
|
1727.2.5
by Aaron Bentley
Handle invalid auth types |
2601 |
child.expect( |
2602 |
'(Enter region name:)|(Select one or more auth types'
|
|
2603 |
' separated by commas:)') |
|
2604 |
if child.match.group(2) is not None: |
|
2605 |
raise AuthNotAccepted('Auth was not compatible.') |
|
1711.6.2
by Aaron Bentley
Test interactive add-cloud against fake juju client. |
2606 |
child.sendline(name) |
2607 |
child.expect('Enter the API endpoint url for the region:') |
|
2608 |
child.sendline(values['endpoint']) |
|
1727.3.2
by Aaron Bentley
Use constant strings where possible. |
2609 |
child.expect('Enter another region\? \(Y/n\):') |
1711.6.2
by Aaron Bentley
Test interactive add-cloud against fake juju client. |
2610 |
if num + 1 < len(cloud['regions']): |
2611 |
child.sendline('y') |
|
2612 |
else: |
|
2613 |
child.sendline('n') |
|
2614 |
if cloud['type'] == 'vsphere': |
|
2615 |
child.expect('Enter the API endpoint url for the cloud:') |
|
2616 |
child.sendline(cloud['endpoint']) |
|
2617 |
for num, (name, values) in enumerate(cloud['regions'].items()): |
|
2618 |
child.expect('Enter region name:') |
|
2619 |
child.sendline(name) |
|
1727.3.2
by Aaron Bentley
Use constant strings where possible. |
2620 |
child.expect('Enter another region\? \(Y/n\):') |
1711.6.2
by Aaron Bentley
Test interactive add-cloud against fake juju client. |
2621 |
if num + 1 < len(cloud['regions']): |
2622 |
child.sendline('y') |
|
2623 |
else: |
|
2624 |
child.sendline('n') |
|
2625 |
||
2626 |
child.expect(pexpect.EOF) |
|
2627 |
except pexpect.TIMEOUT: |
|
2628 |
raise Exception( |
|
2629 |
'Adding cloud failed: pexpect session timed out') |
|
2630 |
||
1556.2.2
by Leo Zhang
changes after review |
2631 |
def show_controller(self, format='json'): |
2632 |
"""Show controller's status."""
|
|
2633 |
return self.get_juju_output('show-controller', '--format', |
|
2634 |
format, include_e=False) |
|
2635 |
||
1645
by Andrew Beach
Added show_machine to the client class. Updated and cleaned the hardware functions in assess_constraints.py. |
2636 |
def show_machine(self, machine): |
2637 |
"""Return data on a machine as a dict."""
|
|
2638 |
text = self.get_juju_output('show-machine', machine, |
|
2639 |
'--format', 'yaml') |
|
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
2640 |
return yaml.safe_load(text) |
1645
by Andrew Beach
Added show_machine to the client class. Updated and cleaned the hardware functions in assess_constraints.py. |
2641 |
|
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
2642 |
def ssh_keys(self, full=False): |
2643 |
"""Give the ssh keys registered for the current model."""
|
|
2644 |
args = [] |
|
2645 |
if full: |
|
2646 |
args.append('--full') |
|
2647 |
return self.get_juju_output('ssh-keys', *args) |
|
2648 |
||
2649 |
def add_ssh_key(self, *keys): |
|
2650 |
"""Add one or more ssh keys to the current model."""
|
|
2651 |
return self.get_juju_output('add-ssh-key', *keys, merge_stderr=True) |
|
2652 |
||
2653 |
def remove_ssh_key(self, *keys): |
|
2654 |
"""Remove one or more ssh keys from the current model."""
|
|
2655 |
return self.get_juju_output('remove-ssh-key', *keys, merge_stderr=True) |
|
2656 |
||
2657 |
def import_ssh_key(self, *keys): |
|
2658 |
"""Import ssh keys from one or more identities to the current model."""
|
|
2659 |
return self.get_juju_output('import-ssh-key', *keys, merge_stderr=True) |
|
2660 |
||
1611.1.2
by Andrew Beach
Finished adding list_disabled_commands and the tests for it. assess_block now uses that method instead of calling get_juju_output directly. |
2661 |
def list_disabled_commands(self): |
1611.1.1
by Andrew Beach
Some basic code for the list_disabled_commands and tests for said methods. |
2662 |
"""List all the commands disabled on the model."""
|
2663 |
raw = self.get_juju_output('list-disabled-commands', |
|
2664 |
'--format', 'yaml') |
|
2665 |
return yaml.safe_load(raw) |
|
2666 |
||
1723.1.1
by Curtis Hovey
Updated assess_block and jujupy to supprt juju 1x blocks. |
2667 |
def disable_command(self, command_set, message=''): |
1725.1.1
by Curtis Hovey
Rename disable_command_* to command_set_*. |
2668 |
"""Disable a command-set."""
|
1723.1.1
by Curtis Hovey
Updated assess_block and jujupy to supprt juju 1x blocks. |
2669 |
return self.juju('disable-command', (command_set, message)) |
1611.1.3
by Andrew Beach
Added disable/enable_command to EnvJujuClient. Not working yet. |
2670 |
|
2671 |
def enable_command(self, args): |
|
1725.1.1
by Curtis Hovey
Rename disable_command_* to command_set_*. |
2672 |
"""Enable a command-set."""
|
1611.1.3
by Andrew Beach
Added disable/enable_command to EnvJujuClient. Not working yet. |
2673 |
return self.juju('enable-command', args) |
2674 |
||
1614.1.18
by Andrew Beach
Adjusted sync_tools to hide the command line options. |
2675 |
def sync_tools(self, local_dir=None): |
2676 |
"""Copy tools into a local directory or model."""
|
|
2677 |
if local_dir is None: |
|
2678 |
return self.juju('sync-tools', ()) |
|
2679 |
else: |
|
2680 |
return self.juju('sync-tools', ('--local-dir', local_dir), |
|
2681 |
include_e=False) |
|
1614.1.9
by Andrew Beach
I have been pushing this stuff around all day (and part of yesterday. I got the assess_metadata test working, although currently its results are not very meaningful. |
2682 |
|
1541.2.6
by Curtis Hovey
Move new behaviours into EnvJujuClient. |
2683 |
|
1646.1.1
by Christopher Lee
Update order of bootstrap args for 2.0+ (RC clients included, w/ test). Updated tests. |
2684 |
class EnvJujuClientRC(EnvJujuClient): |
2685 |
||
2686 |
def get_bootstrap_args( |
|
2687 |
self, upload_tools, config_filename, bootstrap_series=None, |
|
2688 |
credential=None, auto_upgrade=False, metadata_source=None, |
|
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
2689 |
to=None, no_gui=False, agent_version=None): |
1646.1.1
by Christopher Lee
Update order of bootstrap args for 2.0+ (RC clients included, w/ test). Updated tests. |
2690 |
"""Return the bootstrap arguments for the substrate."""
|
2691 |
if self.env.joyent: |
|
2692 |
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
|
|
2693 |
constraints = 'mem=2G cpu-cores=1' |
|
2694 |
else: |
|
2695 |
constraints = 'mem=2G' |
|
2696 |
cloud_region = self.get_cloud_region(self.env.get_cloud(), |
|
2697 |
self.env.get_region()) |
|
2698 |
# Note controller name before cloud_region
|
|
2699 |
args = ['--constraints', constraints, |
|
2700 |
self.env.environment, |
|
2701 |
cloud_region, |
|
2702 |
'--config', config_filename, |
|
2703 |
'--default-model', self.env.environment] |
|
2704 |
if upload_tools: |
|
2705 |
if agent_version is not None: |
|
2706 |
raise ValueError( |
|
2707 |
'agent-version may not be given with upload-tools.') |
|
2708 |
args.insert(0, '--upload-tools') |
|
2709 |
else: |
|
2710 |
if agent_version is None: |
|
2711 |
agent_version = self.get_matching_agent_version() |
|
2712 |
args.extend(['--agent-version', agent_version]) |
|
2713 |
if bootstrap_series is not None: |
|
2714 |
args.extend(['--bootstrap-series', bootstrap_series]) |
|
2715 |
if credential is not None: |
|
2716 |
args.extend(['--credential', credential]) |
|
2717 |
if metadata_source is not None: |
|
2718 |
args.extend(['--metadata-source', metadata_source]) |
|
2719 |
if auto_upgrade: |
|
2720 |
args.append('--auto-upgrade') |
|
2721 |
if to is not None: |
|
2722 |
args.extend(['--to', to]) |
|
1670.1.1
by Martin Packman
Support --no-gui flag to juju bootstrap |
2723 |
if no_gui: |
2724 |
args.append('--no-gui') |
|
1646.1.1
by Christopher Lee
Update order of bootstrap args for 2.0+ (RC clients included, w/ test). Updated tests. |
2725 |
return tuple(args) |
2726 |
||
2727 |
||
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2728 |
class EnvJujuClient1X(EnvJujuClientRC): |
1662.1.5
by Andrew Beach
Removed EnvJujuClient. |
2729 |
"""Base for all 1.x client drivers."""
|
1258.1.3
by Aaron Bentley
Support transitional jujus. |
2730 |
|
1662.1.5
by Andrew Beach
Removed EnvJujuClient. |
2731 |
default_backend = Juju1XBackend |
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
2732 |
|
1465.2.4
by Aaron Bentley
Create a JujuData directly instead of from SimpleEnvironment. |
2733 |
config_class = SimpleEnvironment |
2734 |
||
1692.1.1
by Andrew Beach
ServiceStatus->Status1X |
2735 |
status_class = Status1X |
1662.1.13
by Andrew Beach
Removed EnvJujuClient2B8 and its tests. |
2736 |
|
1662.1.4
by Andrew Beach
Deleted EnvJujuClient2A1 and removed support for testing with it. |
2737 |
# The environments.yaml options that are replaced by bootstrap options.
|
2738 |
# For Juju 1.x, no bootstrap options are used.
|
|
2739 |
bootstrap_replaces = frozenset() |
|
2740 |
||
2741 |
destroy_model_command = 'destroy-environment' |
|
2742 |
||
2743 |
supported_container_types = frozenset([KVM_MACHINE, LXC_MACHINE]) |
|
2744 |
||
2745 |
agent_metadata_url = 'tools-metadata-url' |
|
1221.1.17
by Aaron Bentley
Provide for a separate 2.0-alpha1 client. |
2746 |
|
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
2747 |
_show_status = 'status' |
2748 |
||
1725.1.1
by Curtis Hovey
Rename disable_command_* to command_set_*. |
2749 |
command_set_destroy_model = 'destroy-environment' |
2750 |
||
2751 |
command_set_all = 'all-changes' |
|
1723.1.1
by Curtis Hovey
Updated assess_block and jujupy to supprt juju 1x blocks. |
2752 |
|
1662.1.5
by Andrew Beach
Removed EnvJujuClient. |
2753 |
@classmethod
|
2754 |
def _get_env(cls, env): |
|
2755 |
if isinstance(env, JujuData): |
|
2756 |
raise IncompatibleConfigClass( |
|
2757 |
'JujuData cannot be used with {}'.format(cls.__name__)) |
|
2758 |
return env |
|
1363.7.1
by Aaron Bentley
Implement _shell_environ on backend. |
2759 |
|
1240.2.1
by Aaron Bentley
Support models/cache.yaml |
2760 |
def get_cache_path(self): |
2761 |
return get_cache_path(self.env.juju_home, models=False) |
|
2762 |
||
1221.4.2
by Aaron Bentley
Introduce EnvJujuClient.remove_service. |
2763 |
def remove_service(self, service): |
2764 |
self.juju('destroy-service', (service,)) |
|
2765 |
||
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
2766 |
def backup(self): |
2767 |
environ = self._shell_environ() |
|
2768 |
# juju-backup does not support the -e flag.
|
|
2769 |
environ['JUJU_ENV'] = self.env.environment |
|
2770 |
try: |
|
2771 |
# Mutate os.environ instead of supplying env parameter so Windows
|
|
2772 |
# can search env['PATH']
|
|
2773 |
with scoped_environ(environ): |
|
1326.1.2
by Aaron Bentley
Emit backup command args. |
2774 |
args = ['juju', 'backup'] |
2775 |
log.info(' '.join(args)) |
|
2776 |
output = subprocess.check_output(args) |
|
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
2777 |
except subprocess.CalledProcessError as e: |
2778 |
log.info(e.output) |
|
2779 |
raise
|
|
2780 |
log.info(output) |
|
2781 |
backup_file_pattern = re.compile('(juju-backup-[0-9-]+\.(t|tar.)gz)') |
|
2782 |
match = backup_file_pattern.search(output) |
|
2783 |
if match is None: |
|
2784 |
raise Exception("The backup file was not found in output: %s" % |
|
2785 |
output) |
|
2786 |
backup_file_name = match.group(1) |
|
2787 |
backup_file_path = os.path.abspath(backup_file_name) |
|
2788 |
log.info("State-Server backup at %s", backup_file_path) |
|
2789 |
return backup_file_path |
|
2790 |
||
2791 |
def restore_backup(self, backup_file): |
|
2792 |
return self.get_juju_output('restore', '--constraints', 'mem=2G', |
|
2793 |
backup_file) |
|
2794 |
||
2795 |
def restore_backup_async(self, backup_file): |
|
2796 |
return self.juju_async('restore', ('--constraints', 'mem=2G', |
|
2797 |
backup_file)) |
|
2798 |
||
2799 |
def enable_ha(self): |
|
2800 |
self.juju('ensure-availability', ('-n', '3')) |
|
2801 |
||
1306.1.23
by Curtis Hovey
Added 1.x variations to list-controllers and list-models. |
2802 |
def list_models(self): |
2803 |
"""List the models registered with the current controller."""
|
|
2804 |
log.info('The model is environment {}'.format(self.env.environment)) |
|
2805 |
||
1556.2.3
by Leo Zhang
changes after review |
2806 |
def list_clouds(self, format='json'): |
2807 |
"""List all the available clouds."""
|
|
2808 |
return {} |
|
2809 |
||
2810 |
def show_controller(self, format='json'): |
|
2811 |
"""Show controller's status."""
|
|
2812 |
return {} |
|
2813 |
||
1306.1.23
by Curtis Hovey
Added 1.x variations to list-controllers and list-models. |
2814 |
def get_models(self): |
2815 |
"""return a models dict with a 'models': [] key-value pair."""
|
|
2816 |
return {} |
|
2817 |
||
2818 |
def list_controllers(self): |
|
2819 |
"""List the controllers."""
|
|
2820 |
log.info( |
|
2821 |
'The controller is environment {}'.format(self.env.environment)) |
|
2822 |
||
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
2823 |
@staticmethod
|
2824 |
def get_controller_member_status(info_dict): |
|
2825 |
return info_dict.get('state-server-member-status') |
|
1259.1.1
by Aaron Bentley
Expect controller-member-status in 2.0-alpha2 |
2826 |
|
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
2827 |
def action_fetch(self, id, action=None, timeout="1m"): |
2828 |
"""Fetches the results of the action with the given id.
|
|
2829 |
||
2830 |
Will wait for up to 1 minute for the action results.
|
|
2831 |
The action name here is just used for an more informational error in
|
|
2832 |
cases where it's available.
|
|
2833 |
Returns the yaml output of the fetched action.
|
|
2834 |
"""
|
|
2835 |
# the command has to be "action fetch" so that the -e <env> args are
|
|
2836 |
# placed after "fetch", since that's where action requires them to be.
|
|
2837 |
out = self.get_juju_output("action fetch", id, "--wait", timeout) |
|
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
2838 |
status = yaml.safe_load(out)["status"] |
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
2839 |
if status != "completed": |
2840 |
name = "" |
|
2841 |
if action is not None: |
|
2842 |
name = " " + action |
|
2843 |
raise Exception( |
|
2844 |
"timed out waiting for action%s to complete during fetch" % |
|
2845 |
name) |
|
2846 |
return out |
|
2847 |
||
2848 |
def action_do(self, unit, action, *args): |
|
2849 |
"""Performs the given action on the given unit.
|
|
2850 |
||
2851 |
Action params should be given as args in the form foo=bar.
|
|
2852 |
Returns the id of the queued action.
|
|
2853 |
"""
|
|
2854 |
args = (unit, action) + args |
|
2855 |
||
2856 |
# the command has to be "action do" so that the -e <env> args are
|
|
2857 |
# placed after "do", since that's where action requires them to be.
|
|
2858 |
output = self.get_juju_output("action do", *args) |
|
2859 |
action_id_pattern = re.compile( |
|
2860 |
'Action queued with id: ([a-f0-9\-]{36})') |
|
2861 |
match = action_id_pattern.search(output) |
|
2862 |
if match is None: |
|
2863 |
raise Exception("Action id not found in output: %s" % |
|
2864 |
output) |
|
2865 |
return match.group(1) |
|
2866 |
||
1662.1.13
by Andrew Beach
Removed EnvJujuClient2B8 and its tests. |
2867 |
def run(self, commands, applications): |
2868 |
responses = self.get_juju_output( |
|
2869 |
'run', '--format', 'json', '--service', ','.join(applications), |
|
2870 |
*commands) |
|
2871 |
return json.loads(responses) |
|
2872 |
||
1221.5.2
by Aaron Bentley
Support list-space for EnvJujuClient. |
2873 |
def list_space(self): |
2874 |
return yaml.safe_load(self.get_juju_output('space list')) |
|
2875 |
||
1221.5.4
by Aaron Bentley
Support space create => add-space. |
2876 |
def add_space(self, space): |
2877 |
self.juju('space create', (space),) |
|
2878 |
||
1221.5.6
by Aaron Bentley
Rename subnet add to add-subnet. |
2879 |
def add_subnet(self, subnet, space): |
2880 |
self.juju('subnet add', (subnet, space)) |
|
2881 |
||
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2882 |
def add_user_perms(self, username, models=None, permissions='read'): |
1662.1.16
by Andrew Beach
Used JESNotSupported to remove unsupported functionality from EnvJujuClient1X. |
2883 |
raise JESNotSupported() |
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2884 |
|
2885 |
def grant(self, user_name, permission, model=None): |
|
1662.1.16
by Andrew Beach
Used JESNotSupported to remove unsupported functionality from EnvJujuClient1X. |
2886 |
raise JESNotSupported() |
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2887 |
|
2888 |
def revoke(self, username, models=None, permissions='read'): |
|
1662.1.16
by Andrew Beach
Used JESNotSupported to remove unsupported functionality from EnvJujuClient1X. |
2889 |
raise JESNotSupported() |
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2890 |
|
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
2891 |
def set_model_constraints(self, constraints): |
2892 |
constraint_strings = self._dict_as_option_strings(constraints) |
|
2893 |
return self.juju('set-constraints', constraint_strings) |
|
2894 |
||
1239.1.1
by Aaron Bentley
Implement and use EnvJujuClient.set_config. |
2895 |
def set_config(self, service, options): |
2896 |
option_strings = ['{}={}'.format(*item) for item in options.items()] |
|
2897 |
self.juju('set', (service,) + tuple(option_strings)) |
|
2898 |
||
1221.5.12
by Aaron Bentley
Support get-config. |
2899 |
def get_config(self, service): |
1646.2.1
by Andrew Beach
Removed yaml_loads and replaced all calls with a direct use of yaml.safe_load. |
2900 |
return yaml.safe_load(self.get_juju_output('get', service)) |
1221.5.12
by Aaron Bentley
Support get-config. |
2901 |
|
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
2902 |
def get_model_config(self): |
2903 |
"""Return the value of the environment's configured option."""
|
|
2904 |
return yaml.safe_load(self.get_juju_output('get-env')) |
|
2905 |
||
2906 |
def get_env_option(self, option): |
|
2907 |
"""Return the value of the environment's configured option."""
|
|
2908 |
return self.get_juju_output('get-env', option) |
|
2909 |
||
2910 |
def set_env_option(self, option, value): |
|
2911 |
"""Set the value of the option in the environment."""
|
|
2912 |
option_value = "%s=%s" % (option, value) |
|
2913 |
return self.juju('set-env', (option_value,)) |
|
2914 |
||
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2915 |
def unset_env_option(self, option): |
2916 |
"""Unset the value of the option in the environment."""
|
|
1662.1.16
by Andrew Beach
Used JESNotSupported to remove unsupported functionality from EnvJujuClient1X. |
2917 |
return self.juju('set-env', ('{}='.format(option),)) |
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2918 |
|
1493.1.1
by Martin
Rename methods and variables refering to admin model to new term controller model |
2919 |
def _cmd_model(self, include_e, controller): |
2920 |
if controller: |
|
2921 |
return self.get_controller_model_name() |
|
1427.1.4
by Christopher Lee
Fix failing tests, minor tweak to handle 1.x clients. |
2922 |
elif self.env is None or not include_e: |
2923 |
return None |
|
2924 |
else: |
|
1477.3.1
by Andrew Wilkins
beebop |
2925 |
return unqualified_model_name(self.model_name) |
1427.1.4
by Christopher Lee
Fix failing tests, minor tweak to handle 1.x clients. |
2926 |
|
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
2927 |
def update_user_name(self): |
2928 |
return
|
|
2929 |
||
1657.1.1
by Martin Packman
Work in progress network safe endpoint binding test |
2930 |
def _get_substrate_constraints(self): |
2931 |
if self.env.joyent: |
|
2932 |
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
|
|
2933 |
return 'mem=2G cpu-cores=1' |
|
2934 |
else: |
|
2935 |
return 'mem=2G' |
|
2936 |
||
1579.1.6
by Andrew Beach
Added credential argument to EnvJujuClient.bootstrap and EnvJujuClient.get_bootstrap_args. |
2937 |
def get_bootstrap_args(self, upload_tools, bootstrap_series=None, |
2938 |
credential=None): |
|
1251.2.7
by Aaron Bentley
Update docs |
2939 |
"""Return the bootstrap arguments for the substrate."""
|
1579.1.6
by Andrew Beach
Added credential argument to EnvJujuClient.bootstrap and EnvJujuClient.get_bootstrap_args. |
2940 |
if credential is not None: |
2941 |
raise ValueError( |
|
2942 |
'--credential is not supported by this juju version.') |
|
1260.2.30
by Aaron Bentley
Updates from review |
2943 |
constraints = self._get_substrate_constraints() |
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
2944 |
args = ('--constraints', constraints) |
2945 |
if upload_tools: |
|
2946 |
args = ('--upload-tools',) + args |
|
2947 |
if bootstrap_series is not None: |
|
1699.1.1
by Aaron Bentley
Implement and use SimpleEnvironment and JujuData get_option. |
2948 |
env_val = self.env.get_option('default-series') |
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
2949 |
if bootstrap_series != env_val: |
2950 |
raise BootstrapMismatch( |
|
2951 |
'bootstrap-series', bootstrap_series, 'default-series', |
|
2952 |
env_val) |
|
2953 |
return args |
|
2954 |
||
1662.1.5
by Andrew Beach
Removed EnvJujuClient. |
2955 |
def bootstrap(self, upload_tools=False, bootstrap_series=None): |
2956 |
"""Bootstrap a controller."""
|
|
2957 |
self._check_bootstrap() |
|
2958 |
args = self.get_bootstrap_args(upload_tools, bootstrap_series) |
|
2959 |
self.juju('bootstrap', args, self.env.needs_sudo()) |
|
2960 |
||
2961 |
@contextmanager
|
|
2962 |
def bootstrap_async(self, upload_tools=False): |
|
2963 |
self._check_bootstrap() |
|
2964 |
args = self.get_bootstrap_args(upload_tools) |
|
2965 |
with self.juju_async('bootstrap', args): |
|
2966 |
yield
|
|
2967 |
log.info('Waiting for bootstrap of {}.'.format( |
|
2968 |
self.env.environment)) |
|
2969 |
||
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
2970 |
def get_jes_command(self): |
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
2971 |
raise JESNotSupported() |
2972 |
||
2973 |
def enable_jes(self): |
|
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
2974 |
raise JESNotSupported() |
2975 |
||
1540.3.3
by Christopher Lee
Separate out controller update and model. Only update 'self' model not all models under controller |
2976 |
def upgrade_juju(self, force_version=True): |
2977 |
args = () |
|
2978 |
if force_version: |
|
2979 |
version = self.get_matching_agent_version(no_build=True) |
|
2980 |
args += ('--version', version) |
|
2981 |
if self.env.local: |
|
2982 |
args += ('--upload-tools',) |
|
2983 |
self._upgrade_juju(args) |
|
1540.3.1
by Christopher Lee
Update upgrade methods to work with juju 2.0. Upgrades controller first then any other hosted models. |
2984 |
|
1363.4.3
by Aaron Bentley
Provide config, rather than a client, to create_model. |
2985 |
def make_model_config(self): |
2986 |
config_dict = make_safe_config(self) |
|
2987 |
# Strip unneeded variables.
|
|
2988 |
return config_dict |
|
2989 |
||
1363.4.4
by Aaron Bentley
Rename create_model to add_model. |
2990 |
def _add_model(self, model_name, config_file): |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
2991 |
seen_cmd = self.get_jes_command() |
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
2992 |
if seen_cmd == SYSTEM: |
1363.4.3
by Aaron Bentley
Provide config, rather than a client, to create_model. |
2993 |
controller_option = ('-s', self.env.environment) |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
2994 |
else: |
1363.4.3
by Aaron Bentley
Provide config, rather than a client, to create_model. |
2995 |
controller_option = ('-c', self.env.environment) |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
2996 |
self.juju(_jes_cmds[seen_cmd]['create'], controller_option + ( |
1363.4.3
by Aaron Bentley
Provide config, rather than a client, to create_model. |
2997 |
model_name, '--config', config_file), include_e=False) |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
2998 |
|
1221.1.15
by Aaron Bentley
Implement and use destroy_model for destroying models. |
2999 |
def destroy_model(self): |
3000 |
"""With JES enabled, destroy-environment destroys the model."""
|
|
1685.1.1
by Andrew Beach
Started unplugging kill-controller from _jes_cmds. That part is actually done, getting the new part to work is trickier. |
3001 |
return self.destroy_environment(force=False) |
3002 |
||
3003 |
def kill_controller(self): |
|
1688.1.16
by Andrew Beach
Updated doc-strings. |
3004 |
"""Destroy the environment, with force. Hard kill option.
|
3005 |
||
3006 |
:return: Subprocess's exit code."""
|
|
1685.1.5
by Andrew Beach
Updated kill/destroy_controller. |
3007 |
return self.juju( |
3008 |
'destroy-environment', (self.env.environment, '--force', '-y'), |
|
3009 |
check=False, include_e=False, timeout=get_teardown_timeout(self)) |
|
1221.1.15
by Aaron Bentley
Implement and use destroy_model for destroying models. |
3010 |
|
1688.1.15
by Andrew Beach
Added --destroy-all-models flag to destroy-controller. |
3011 |
def destroy_controller(self, all_models=False): |
1688.1.16
by Andrew Beach
Updated doc-strings. |
3012 |
"""Destroy the environment, with force. Soft kill option.
|
3013 |
||
3014 |
:param all_models: Ignored.
|
|
3015 |
:raises: subprocess.CalledProcessError if the operation fails."""
|
|
1685.1.5
by Andrew Beach
Updated kill/destroy_controller. |
3016 |
return self.juju( |
3017 |
'destroy-environment', (self.env.environment, '-y'), |
|
3018 |
include_e=False, timeout=get_teardown_timeout(self)) |
|
1685.1.4
by Andrew Beach
Added the soft kill destroy-controller. |
3019 |
|
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
3020 |
def destroy_environment(self, force=True, delete_jenv=False): |
3021 |
if force: |
|
3022 |
force_arg = ('--force',) |
|
3023 |
else: |
|
3024 |
force_arg = () |
|
3025 |
exit_status = self.juju( |
|
3026 |
'destroy-environment', |
|
3027 |
(self.env.environment,) + force_arg + ('-y',), |
|
1685.1.1
by Andrew Beach
Started unplugging kill-controller from _jes_cmds. That part is actually done, getting the new part to work is trickier. |
3028 |
check=False, include_e=False, |
1535.1.4
by Curtis Hovey
Extracted get_teardown_timeout. |
3029 |
timeout=get_teardown_timeout(self)) |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
3030 |
if delete_jenv: |
3031 |
jenv_path = get_jenv_path(self.env.juju_home, self.env.environment) |
|
3032 |
ensure_deleted(jenv_path) |
|
3033 |
return exit_status |
|
3034 |
||
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
3035 |
def _get_models(self): |
3036 |
"""return a list of model dicts."""
|
|
1452.1.1
by Curtis Hovey
Ensure _get_models() is compatible with 1.x |
3037 |
try: |
3038 |
return yaml.safe_load(self.get_juju_output( |
|
3039 |
'environments', '-s', self.env.environment, '--format', 'yaml', |
|
3040 |
include_e=False)) |
|
3041 |
except subprocess.CalledProcessError: |
|
3042 |
# This *private* method attempts to use a 1.25 JES feature.
|
|
3043 |
# The JES design is dead. The private method is not used to
|
|
3044 |
# directly test juju cli; the failure is not a contract violation.
|
|
3045 |
log.info('Call to JES juju environments failed, falling back.') |
|
3046 |
return [] |
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
3047 |
|
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
3048 |
def get_model_uuid(self): |
1662.1.16
by Andrew Beach
Used JESNotSupported to remove unsupported functionality from EnvJujuClient1X. |
3049 |
raise JESNotSupported() |
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
3050 |
|
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
3051 |
def deploy_bundle(self, bundle, timeout=_DEFAULT_BUNDLE_TIMEOUT): |
3052 |
"""Deploy bundle using deployer for Juju 1.X version."""
|
|
3053 |
self.deployer(bundle, timeout=timeout) |
|
3054 |
||
1341.2.10
by Aaron Bentley
Fix deployer implementation. |
3055 |
def deployer(self, bundle_template, name=None, deploy_delay=10, |
3056 |
timeout=3600): |
|
1460.1.2
by Aaron Bentley
Updates from review |
3057 |
"""Deploy a bundle using deployer."""
|
1341.2.10
by Aaron Bentley
Fix deployer implementation. |
3058 |
bundle = self.format_bundle(bundle_template) |
1363.1.1
by Seman
Updated Deployer to support Juju 2.X. |
3059 |
args = ( |
3060 |
'--debug', |
|
3061 |
'--deploy-delay', str(deploy_delay), |
|
3062 |
'--timeout', str(timeout), |
|
3063 |
'--config', bundle, |
|
3064 |
)
|
|
3065 |
if name: |
|
3066 |
args += (name,) |
|
3067 |
self.juju('deployer', args, self.env.needs_sudo()) |
|
3068 |
||
1662.1.5
by Andrew Beach
Removed EnvJujuClient. |
3069 |
def deploy(self, charm, repository=None, to=None, series=None, |
3070 |
service=None, force=False, storage=None, constraints=None): |
|
3071 |
args = [charm] |
|
3072 |
if repository is not None: |
|
3073 |
args.extend(['--repository', repository]) |
|
3074 |
if to is not None: |
|
3075 |
args.extend(['--to', to]) |
|
3076 |
if service is not None: |
|
3077 |
args.extend([service]) |
|
3078 |
if storage is not None: |
|
3079 |
args.extend(['--storage', storage]) |
|
3080 |
if constraints is not None: |
|
3081 |
args.extend(['--constraints', constraints]) |
|
3082 |
return self.juju('deploy', tuple(args)) |
|
3083 |
||
1369.1.1
by Aaron Bentley
Implement upgrade_charm, switch industrial_test to it. |
3084 |
def upgrade_charm(self, service, charm_path=None): |
3085 |
args = (service,) |
|
3086 |
if charm_path is not None: |
|
3087 |
repository = os.path.dirname(os.path.dirname(charm_path)) |
|
3088 |
args = args + ('--repository', repository) |
|
3089 |
self.juju('upgrade-charm', args) |
|
3090 |
||
1662.1.7
by Andrew Beach
Removed EnvJujuClient2B2. |
3091 |
def get_controller_client(self): |
3092 |
"""Return a client for the controller model. May return self."""
|
|
3093 |
return self |
|
3094 |
||
3095 |
def get_controller_model_name(self): |
|
3096 |
"""Return the name of the 'controller' model.
|
|
3097 |
||
1662.1.9
by Andrew Beach
Removed the JES code from EnvJujuClient1X.get_controller_model_name. |
3098 |
Return the name of the 1.x environment."""
|
1662.1.7
by Andrew Beach
Removed EnvJujuClient2B2. |
3099 |
return self.env.environment |
3100 |
||
1306.1.5
by Curtis Hovey
Added get_controller_endpoint for get_endpoints. |
3101 |
def get_controller_endpoint(self): |
3102 |
"""Return the address of the state-server leader."""
|
|
1306.1.19
by Curtis Hovey
Fix calls to show-controller with include_e=False. |
3103 |
endpoint = self.get_juju_output('api-endpoints') |
1310.1.2
by Curtis Hovey
Address ipv6 split address and port. |
3104 |
address, port = split_address_port(endpoint) |
1306.1.5
by Curtis Hovey
Added get_controller_endpoint for get_endpoints. |
3105 |
return address |
3106 |
||
1258.2.5
by Curtis Hovey
Added upgrade_mongo to EnvJujuClient. |
3107 |
def upgrade_mongo(self): |
3108 |
raise UpgradeMongoNotSupported() |
|
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
3109 |
|
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
3110 |
def create_cloned_environment( |
3111 |
self, cloned_juju_home, controller_name, user_name=None): |
|
3112 |
"""Create a cloned environment.
|
|
3113 |
||
3114 |
`user_name` is unused in this version of juju.
|
|
3115 |
"""
|
|
3116 |
user_client = self.clone(env=self.env.clone()) |
|
3117 |
user_client.env.juju_home = cloned_juju_home |
|
3118 |
# New user names the controller.
|
|
3119 |
user_client.env.controller = Controller(controller_name) |
|
3120 |
return user_client |
|
3121 |
||
1466.2.1
by Leo Zhang
Fit version 1.x |
3122 |
def add_storage(self, unit, storage_type, amount="1"): |
3123 |
"""Add storage instances to service.
|
|
3124 |
||
3125 |
Only type 'disk' is able to add instances.
|
|
3126 |
"""
|
|
3127 |
self.juju('storage add', (unit, storage_type + "=" + amount)) |
|
3128 |
||
3129 |
def list_storage(self): |
|
3130 |
"""Return the storage list."""
|
|
3131 |
return self.get_juju_output('storage list', '--format', 'json') |
|
3132 |
||
3133 |
def list_storage_pool(self): |
|
3134 |
"""Return the list of storage pool."""
|
|
3135 |
return self.get_juju_output('storage pool list', '--format', 'json') |
|
3136 |
||
3137 |
def create_storage_pool(self, name, provider, size): |
|
3138 |
"""Create storage pool."""
|
|
3139 |
self.juju('storage pool create', |
|
3140 |
(name, provider, |
|
3141 |
'size={}'.format(size))) |
|
3142 |
||
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
3143 |
def ssh_keys(self, full=False): |
3144 |
"""Give the ssh keys registered for the current model."""
|
|
3145 |
args = [] |
|
3146 |
if full: |
|
3147 |
args.append('--full') |
|
1581.1.1
by Martin Packman
Make assess_ssh_keys 1.25 compatible and update for landed fix |
3148 |
return self.get_juju_output('authorized-keys list', *args) |
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
3149 |
|
3150 |
def add_ssh_key(self, *keys): |
|
3151 |
"""Add one or more ssh keys to the current model."""
|
|
1581.1.1
by Martin Packman
Make assess_ssh_keys 1.25 compatible and update for landed fix |
3152 |
return self.get_juju_output('authorized-keys add', *keys, |
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
3153 |
merge_stderr=True) |
3154 |
||
3155 |
def remove_ssh_key(self, *keys): |
|
3156 |
"""Remove one or more ssh keys from the current model."""
|
|
1581.1.1
by Martin Packman
Make assess_ssh_keys 1.25 compatible and update for landed fix |
3157 |
return self.get_juju_output('authorized-keys delete', *keys, |
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
3158 |
merge_stderr=True) |
3159 |
||
3160 |
def import_ssh_key(self, *keys): |
|
3161 |
"""Import ssh keys from one or more identities to the current model."""
|
|
1581.1.1
by Martin Packman
Make assess_ssh_keys 1.25 compatible and update for landed fix |
3162 |
return self.get_juju_output('authorized-keys import', *keys, |
1553.1.1
by Martin Packman
Add support for ssh key related commands to jujupy |
3163 |
merge_stderr=True) |
3164 |
||
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
3165 |
def list_disabled_commands(self): |
3166 |
"""List all the commands disabled on the model."""
|
|
3167 |
raw = self.get_juju_output('block list', '--format', 'yaml') |
|
3168 |
return yaml.safe_load(raw) |
|
3169 |
||
1723.1.1
by Curtis Hovey
Updated assess_block and jujupy to supprt juju 1x blocks. |
3170 |
def disable_command(self, command_set, message=''): |
1725.1.1
by Curtis Hovey
Rename disable_command_* to command_set_*. |
3171 |
"""Disable a command-set."""
|
1723.1.1
by Curtis Hovey
Updated assess_block and jujupy to supprt juju 1x blocks. |
3172 |
return self.juju('block {}'.format(command_set), (message, )) |
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
3173 |
|
3174 |
def enable_command(self, args): |
|
1725.1.1
by Curtis Hovey
Rename disable_command_* to command_set_*. |
3175 |
"""Enable a command-set."""
|
1662.1.14
by Andrew Beach
Removed EnvJujuClient2B9. |
3176 |
return self.juju('unblock', args) |
3177 |
||
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
3178 |
|
3179 |
class EnvJujuClient22(EnvJujuClient1X): |
|
953.3.9
by Nate Finch
more code review changes |
3180 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
3181 |
used_feature_flags = frozenset(['actions']) |
953.3.9
by Nate Finch
more code review changes |
3182 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
3183 |
def __init__(self, *args, **kwargs): |
3184 |
super(EnvJujuClient22, self).__init__(*args, **kwargs) |
|
3185 |
self.feature_flags.add('actions') |
|
953.3.9
by Nate Finch
more code review changes |
3186 |
|
3187 |
||
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
3188 |
class EnvJujuClient25(EnvJujuClient1X): |
3189 |
"""Drives Juju 2.5-series clients."""
|
|
3190 |
||
3191 |
used_feature_flags = frozenset() |
|
1221.1.8
by Aaron Bentley
Implement EnvJujuClient.clone. |
3192 |
|
1162.2.20
by Aaron Bentley
Implement disable_jes. |
3193 |
def disable_jes(self): |
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
3194 |
self.feature_flags.discard('jes') |
1074.2.1
by Aaron Bentley
Add EnvJujuClient support for Juju 2.6. |
3195 |
|
3196 |
||
957.2.1
by Aaron Bentley
Treat cloudsigma as provisional in 2.5 |
3197 |
class EnvJujuClient24(EnvJujuClient25): |
1662.1.1
by Andrew Beach
Cut out EnvJujuClient26. Will have to split it into smaller pieces for merging. |
3198 |
"""Similar to EnvJujuClient25."""
|
1044.1.9
by Aaron Bentley
Update and add tests. |
3199 |
|
1256.1.2
by Aaron Bentley
Expect old add-machine behaviour from 1.24.x and earlier. |
3200 |
def add_ssh_machines(self, machines): |
3201 |
for machine in machines: |
|
3202 |
self.juju('add-machine', ('ssh:' + machine,)) |
|
3203 |
||
957.2.1
by Aaron Bentley
Treat cloudsigma as provisional in 2.5 |
3204 |
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3205 |
def get_local_root(juju_home, env): |
3206 |
return os.path.join(juju_home, env.environment) |
|
3207 |
||
3208 |
||
663.1.8
by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env. |
3209 |
def bootstrap_from_env(juju_home, client): |
3210 |
with temp_bootstrap_env(juju_home, client): |
|
3211 |
client.bootstrap() |
|
3212 |
||
3213 |
||
796.2.4
by John George
Added support for setting the juju path, series and agent_url. |
3214 |
def quickstart_from_env(juju_home, client, bundle): |
3215 |
with temp_bootstrap_env(juju_home, client): |
|
3216 |
client.quickstart(bundle) |
|
3217 |
||
3218 |
||
696.1.10
by Aaron Bentley
Move uniquify_local to jujupy. |
3219 |
def uniquify_local(env): |
3220 |
"""Ensure that local environments have unique port settings.
|
|
3221 |
||
3222 |
This allows local environments to be duplicated despite
|
|
3223 |
https://bugs.launchpad.net/bugs/1382131
|
|
3224 |
"""
|
|
3225 |
if not env.local: |
|
3226 |
return
|
|
3227 |
port_defaults = { |
|
3228 |
'api-port': 17070, |
|
3229 |
'state-port': 37017, |
|
3230 |
'storage-port': 8040, |
|
3231 |
'syslog-port': 6514, |
|
3232 |
}
|
|
1674.1.16
by Aaron Bentley
Switch call sites to update_config. |
3233 |
new_config = {} |
696.1.10
by Aaron Bentley
Move uniquify_local to jujupy. |
3234 |
for key, default in port_defaults.items(): |
1699.1.2
by Aaron Bentley
Convert direct config access to get_option. |
3235 |
new_config[key] = env.get_option(key, default) + 1 |
1674.1.16
by Aaron Bentley
Switch call sites to update_config. |
3236 |
env.update_config(new_config) |
696.1.10
by Aaron Bentley
Move uniquify_local to jujupy. |
3237 |
|
3238 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
3239 |
def dump_environments_yaml(juju_home, config): |
1600.1.1
by Andrew Beach
Added docstrings to jujupy.py. |
3240 |
"""Dump yaml data to the environment file.
|
3241 |
||
3242 |
:param juju_home: Path to the JUJU_HOME directory.
|
|
3243 |
:param config: Dictionary repersenting yaml data to dump."""
|
|
1044.1.9
by Aaron Bentley
Update and add tests. |
3244 |
environments_path = get_environments_path(juju_home) |
3245 |
with open(environments_path, 'w') as config_file: |
|
3246 |
yaml.safe_dump(config, config_file) |
|
3247 |
||
3248 |
||
663.1.8
by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env. |
3249 |
@contextmanager
|
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
3250 |
def _temp_env(new_config, parent=None, set_home=True): |
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
3251 |
"""Use the supplied config as juju environment.
|
3252 |
||
3253 |
This is not a fully-formed version for bootstrapping. See
|
|
3254 |
temp_bootstrap_env.
|
|
3255 |
"""
|
|
3256 |
with temp_dir(parent) as temp_juju_home: |
|
1044.1.9
by Aaron Bentley
Update and add tests. |
3257 |
dump_environments_yaml(temp_juju_home, new_config) |
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
3258 |
if set_home: |
1695.2.2
by Andrew Beach
Bits of clean-up that happened while I was working on finding public-clouds.yaml. |
3259 |
with scoped_environ(): |
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
3260 |
os.environ['JUJU_HOME'] = temp_juju_home |
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
3261 |
os.environ['JUJU_DATA'] = temp_juju_home |
1695.2.2
by Andrew Beach
Bits of clean-up that happened while I was working on finding public-clouds.yaml. |
3262 |
yield temp_juju_home |
3263 |
else: |
|
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
3264 |
yield temp_juju_home |
3265 |
||
3266 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
3267 |
def jes_home_path(juju_home, dir_name): |
1048.1.3
by Aaron Bentley
Add make_jes_home and jes_home_path to tests. |
3268 |
return os.path.join(juju_home, 'jes-homes', dir_name) |
1044.1.9
by Aaron Bentley
Update and add tests. |
3269 |
|
3270 |
||
1240.2.1
by Aaron Bentley
Support models/cache.yaml |
3271 |
def get_cache_path(juju_home, models=False): |
3272 |
if models: |
|
3273 |
root = os.path.join(juju_home, 'models') |
|
3274 |
else: |
|
3275 |
root = os.path.join(juju_home, 'environments') |
|
3276 |
return os.path.join(root, 'cache.yaml') |
|
1050.2.2
by Aaron Bentley
Retain config.yaml instead of .jenv for JES. |
3277 |
|
3278 |
||
1242.3.16
by Aaron Bentley
Make agent-version omission client-dependent. |
3279 |
def make_safe_config(client): |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3280 |
config = dict(client.env.config) |
1242.3.18
by Aaron Bentley
Rename bootstrap_supports to bootstrap_replaces. |
3281 |
if 'agent-version' in client.bootstrap_replaces: |
1242.3.16
by Aaron Bentley
Make agent-version omission client-dependent. |
3282 |
config.pop('agent-version', None) |
3283 |
else: |
|
1242.3.10
by Aaron Bentley
Base support for agent-version. |
3284 |
config['agent-version'] = client.get_matching_agent_version() |
709.1.1
by Aaron Bentley
Always set test-mode to True. |
3285 |
# AFAICT, we *always* want to set test-mode to True. If we ever find a
|
3286 |
# use-case where we don't, we can make this optional.
|
|
3287 |
config['test-mode'] = True |
|
990.3.1
by Curtis Hovey
Ensure the env name is in the config. |
3288 |
# Explicitly set 'name', which Juju implicitly sets to env.environment to
|
3289 |
# ensure MAASAccount knows what the name will be.
|
|
1477.3.1
by Andrew Wilkins
beebop |
3290 |
config['name'] = unqualified_model_name(client.env.environment) |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3291 |
if config['type'] == 'local': |
1193.2.5
by Aaron Bentley
Remove EnvJujuClient.juju_home completely. |
3292 |
config.setdefault('root-dir', get_local_root(client.env.juju_home, |
995.1.14
by Aaron Bentley
Destroy environments. |
3293 |
client.env)) |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3294 |
# MongoDB requires a lot of free disk space, and the only
|
3295 |
# visible error message is from "juju bootstrap":
|
|
3296 |
# "cannot initiate replication set" if disk space is low.
|
|
3297 |
# What "low" exactly means, is unclear, but 8GB should be
|
|
3298 |
# enough.
|
|
3299 |
ensure_dir(config['root-dir']) |
|
3300 |
check_free_disk_space(config['root-dir'], 8000000, "MongoDB files") |
|
3301 |
if client.env.kvm: |
|
3302 |
check_free_disk_space( |
|
3303 |
"/var/lib/uvtool/libvirt/images", 2000000, |
|
3304 |
"KVM disk files") |
|
3305 |
else: |
|
3306 |
check_free_disk_space( |
|
3307 |
"/var/lib/lxc", 2000000, "LXC containers") |
|
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
3308 |
return config |
3309 |
||
3310 |
||
3311 |
@contextmanager
|
|
1242.3.16
by Aaron Bentley
Make agent-version omission client-dependent. |
3312 |
def temp_bootstrap_env(juju_home, client, set_home=True, permanent=False): |
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
3313 |
"""Create a temporary environment for bootstrapping.
|
3314 |
||
3315 |
This involves creating a temporary juju home directory and returning its
|
|
3316 |
location.
|
|
3317 |
||
1695.2.1
by Andrew Beach
Trying to get public-clouds.yaml into the temperary directory used for tests. |
3318 |
:param juju_home: The current JUJU_HOME value.
|
3319 |
:param client: The client being prepared for bootstrapping.
|
|
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
3320 |
:param set_home: Set JUJU_HOME to match the temporary home in this
|
3321 |
context. If False, juju_home should be supplied to bootstrap.
|
|
1695.2.1
by Andrew Beach
Trying to get public-clouds.yaml into the temperary directory used for tests. |
3322 |
:param permanent: If permanent, the environment is kept afterwards.
|
1695.2.7
by Andrew Beach
Fixed a few bits of documentation. |
3323 |
Otherwise the environment is deleted when exiting the context.
|
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
3324 |
"""
|
3325 |
new_config = { |
|
1251.2.6
by Aaron Bentley
Remove spurious change. |
3326 |
'environments': {client.env.environment: make_safe_config(client)}} |
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
3327 |
# Always bootstrap a matching environment.
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3328 |
jenv_path = get_jenv_path(juju_home, client.env.environment) |
1044.1.9
by Aaron Bentley
Update and add tests. |
3329 |
if permanent: |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
3330 |
context = client.env.make_jes_home( |
3331 |
juju_home, client.env.environment, new_config) |
|
1044.1.9
by Aaron Bentley
Update and add tests. |
3332 |
else: |
3333 |
context = _temp_env(new_config, juju_home, set_home) |
|
3334 |
with context as temp_juju_home: |
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3335 |
if os.path.lexists(jenv_path): |
3336 |
raise Exception('%s already exists!' % jenv_path) |
|
3337 |
new_jenv_path = get_jenv_path(temp_juju_home, client.env.environment) |
|
3338 |
# Create a symlink to allow access while bootstrapping, and to reduce
|
|
3339 |
# races. Can't use a hard link because jenv doesn't exist until
|
|
3340 |
# partway through bootstrap.
|
|
3341 |
ensure_dir(os.path.join(juju_home, 'environments')) |
|
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
3342 |
# Skip creating symlink where not supported (i.e. Windows).
|
1048.1.4
by Aaron Bentley
Improve condition order. |
3343 |
if not permanent and getattr(os, 'symlink', None) is not None: |
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
3344 |
os.symlink(new_jenv_path, jenv_path) |
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
3345 |
old_juju_home = client.env.juju_home |
3346 |
client.env.juju_home = temp_juju_home |
|
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
3347 |
try: |
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
3348 |
yield temp_juju_home |
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
3349 |
finally: |
1044.1.9
by Aaron Bentley
Update and add tests. |
3350 |
if not permanent: |
3351 |
# replace symlink with file before deleting temp home.
|
|
3352 |
try: |
|
3353 |
os.rename(new_jenv_path, jenv_path) |
|
3354 |
except OSError as e: |
|
3355 |
if e.errno != errno.ENOENT: |
|
3356 |
raise
|
|
3357 |
# Remove dangling symlink
|
|
1703.1.6
by Andrew Beach
Renamed the function to skip_on_missing_file. |
3358 |
with skip_on_missing_file(): |
1048.1.5
by Aaron Bentley
Updates from review. |
3359 |
os.unlink(jenv_path) |
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
3360 |
client.env.juju_home = old_juju_home |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
3361 |
|
3362 |
||
1153.4.3
by Martin Packman
Changes from review by abentley |
3363 |
def get_machine_dns_name(client, machine, timeout=600): |
3364 |
"""Wait for dns-name on a juju machine."""
|
|
3365 |
for status in client.status_until(timeout=timeout): |
|
3366 |
try: |
|
3367 |
return _dns_name_for_machine(status, machine) |
|
3368 |
except KeyError: |
|
1153.4.4
by Martin Packman
Log in jujupy context |
3369 |
log.debug("No dns-name yet for machine %s", machine) |
1153.4.3
by Martin Packman
Changes from review by abentley |
3370 |
|
3371 |
||
3372 |
def _dns_name_for_machine(status, machine): |
|
3373 |
host = status.status['machines'][machine]['dns-name'] |
|
1167.2.1
by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use |
3374 |
if is_ipv6_address(host): |
3375 |
log.warning("Selected IPv6 address for machine %s: %r", machine, host) |
|
3376 |
return host |
|
1153.4.3
by Martin Packman
Changes from review by abentley |
3377 |
|
3378 |
||
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
3379 |
class Controller: |
1600.1.2
by Andrew Beach
Various spelling mistakes. |
3380 |
"""Represents the controller for a model or models."""
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
3381 |
|
3382 |
def __init__(self, name): |
|
3383 |
self.name = name |
|
1711.3.3
by Aaron Bentley
Let controller decide whether to supply cloud/region and credentials. |
3384 |
self.explicit_region = False |
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
3385 |
|
3386 |
||
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3387 |
class GroupReporter: |
3388 |
||
3389 |
def __init__(self, stream, expected): |
|
3390 |
self.stream = stream |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
3391 |
self.expected = expected |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3392 |
self.last_group = None |
3393 |
self.ticks = 0 |
|
922.1.1
by Martin Packman
Add wrapping at 80 characters to group reporter |
3394 |
self.wrap_offset = 0 |
922.1.2
by Martin Packman
Use width of 79 and add test suggested by abentley in review |
3395 |
self.wrap_width = 79 |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3396 |
|
3397 |
def _write(self, string): |
|
3398 |
self.stream.write(string) |
|
3399 |
self.stream.flush() |
|
3400 |
||
769.1.1
by Martin Packman
Add finish method to GroupReporter to end dotties |
3401 |
def finish(self): |
3402 |
if self.last_group: |
|
3403 |
self._write("\n") |
|
3404 |
||
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3405 |
def update(self, group): |
3406 |
if group == self.last_group: |
|
922.1.1
by Martin Packman
Add wrapping at 80 characters to group reporter |
3407 |
if (self.wrap_offset + self.ticks) % self.wrap_width == 0: |
3408 |
self._write("\n") |
|
3409 |
self._write("." if self.ticks or not self.wrap_offset else " .") |
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3410 |
self.ticks += 1 |
3411 |
return
|
|
3412 |
value_listing = [] |
|
3413 |
for value, entries in sorted(group.items()): |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
3414 |
if value == self.expected: |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3415 |
continue
|
3416 |
value_listing.append('%s: %s' % (value, ', '.join(entries))) |
|
3417 |
string = ' | '.join(value_listing) |
|
930.1.1
by Martin Packman
Fix wrapping of dots when printing updated group status |
3418 |
lead_length = len(string) + 1 |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
3419 |
if self.last_group: |
3420 |
string = "\n" + string |
|
3421 |
self._write(string) |
|
3422 |
self.last_group = group |
|
3423 |
self.ticks = 0 |
|
922.1.1
by Martin Packman
Add wrapping at 80 characters to group reporter |
3424 |
self.wrap_offset = lead_length if lead_length < self.wrap_width else 0 |