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, |
|
6 |
)
|
|
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
7 |
from contextlib import ( |
8 |
contextmanager, |
|
9 |
nested, |
|
953.3.9
by Nate Finch
more code review changes |
10 |
)
|
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
11 |
from copy import deepcopy |
2
by Aaron Bentley
Added initial deploy_stack. |
12 |
from cStringIO import StringIO |
768.1.1
by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment. |
13 |
from datetime import timedelta |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
14 |
import errno |
751.1.1
by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt |
15 |
from itertools import chain |
715.2.1
by John George
add debug logging option to show command and output from juju calls made from get_juju_output |
16 |
import logging |
161
by Curtis Hovey
Windows and py3 compatability. |
17 |
import os |
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
18 |
import re |
1260.2.13
by Aaron Bentley
Fix lint. |
19 |
from shutil import rmtree |
2
by Aaron Bentley
Added initial deploy_stack. |
20 |
import subprocess |
21 |
import sys |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
22 |
from tempfile import NamedTemporaryFile |
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
23 |
import time |
2
by Aaron Bentley
Added initial deploy_stack. |
24 |
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
25 |
import yaml |
26 |
||
27 |
from jujuconfig import ( |
|
28 |
get_environments_path, |
|
29 |
get_jenv_path, |
|
777.2.1
by Aaron Bentley
Always delete jenv in industrial test. |
30 |
get_juju_home, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
31 |
get_selected_environment, |
953.3.9
by Nate Finch
more code review changes |
32 |
)
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
33 |
from utility import ( |
34 |
check_free_disk_space, |
|
777.2.1
by Aaron Bentley
Always delete jenv in industrial test. |
35 |
ensure_deleted, |
1091.4.1
by James Tunnicliffe
Merged upstream |
36 |
ensure_dir, |
1167.2.1
by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use |
37 |
is_ipv6_address, |
763.1.2
by Curtis Hovey
Added puase to sleep after ha, but remove it for tests. |
38 |
pause, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
39 |
scoped_environ, |
1310.1.2
by Curtis Hovey
Address ipv6 split address and port. |
40 |
split_address_port, |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
41 |
temp_dir, |
42 |
until_timeout, |
|
953.3.9
by Nate Finch
more code review changes |
43 |
)
|
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
44 |
|
2
by Aaron Bentley
Added initial deploy_stack. |
45 |
|
1092.2.2
by Aaron Bentley
Fix lint. |
46 |
__metaclass__ = type |
47 |
||
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
48 |
AGENTS_READY = set(['started', 'idle']) |
163
by Curtis Hovey
Extracted the windows command incase it needs to be reused. |
49 |
WIN_JUJU_CMD = os.path.join('\\', 'Progra~2', 'Juju', 'juju.exe') |
50 |
||
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
51 |
JUJU_DEV_FEATURE_FLAGS = 'JUJU_DEV_FEATURE_FLAGS' |
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
52 |
CONTROLLER = 'controller' |
53 |
KILL_CONTROLLER = 'kill-controller' |
|
54 |
SYSTEM = 'system' |
|
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
55 |
|
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
56 |
_DEFAULT_BUNDLE_TIMEOUT = 3600 |
57 |
||
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
58 |
_jes_cmds = {KILL_CONTROLLER: { |
1199
by Curtis Hovey
Revert lp:~sinzui/juju-ci-tools/kill-controller-command r1198 because it exposes that controllers are |
59 |
'create': 'create-environment', |
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
60 |
'kill': KILL_CONTROLLER, |
1306.1.2
by Curtis Hovey
Fix missing brace. |
61 |
}}
|
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
62 |
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 |
63 |
_jes_cmds[super_cmd] = { |
64 |
'create': '{} create-environment'.format(super_cmd), |
|
65 |
'kill': '{} kill'.format(super_cmd), |
|
1306.1.1
by Curtis Hovey
Save spike to get leader and followers. |
66 |
}
|
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
67 |
|
1091.4.1
by James Tunnicliffe
Merged upstream |
68 |
log = logging.getLogger("jujupy") |
69 |
||
163
by Curtis Hovey
Extracted the windows command incase it needs to be reused. |
70 |
|
1069.1.1
by Aaron Bentley
Replace timeout utility in _full_args. |
71 |
def get_timeout_path(): |
72 |
import timeout |
|
73 |
return os.path.abspath(timeout.__file__) |
|
74 |
||
75 |
||
76 |
def get_timeout_prefix(duration, timeout_path=None): |
|
77 |
"""Return extra arguments to run a command with a timeout."""
|
|
78 |
if timeout_path is None: |
|
79 |
timeout_path = get_timeout_path() |
|
80 |
return (sys.executable, timeout_path, '%.2f' % duration, '--') |
|
81 |
||
82 |
||
953.3.8
by Nate Finch
more review changes |
83 |
def parse_new_state_server_from_error(error): |
976.2.1
by Aaron Bentley
Fix parse_new_state_server_from_error |
84 |
err_str = str(error) |
85 |
output = getattr(error, 'output', None) |
|
86 |
if output is not None: |
|
87 |
err_str += output |
|
88 |
matches = re.findall(r'Attempting to connect to (.*):22', err_str) |
|
953.3.8
by Nate Finch
more review changes |
89 |
if matches: |
90 |
return matches[-1] |
|
91 |
return None |
|
92 |
||
93 |
||
2
by Aaron Bentley
Added initial deploy_stack. |
94 |
class ErroredUnit(Exception): |
95 |
||
301.1.3
by Aaron Bentley
Remove environment name from log messages and errors. |
96 |
def __init__(self, unit_name, state): |
97 |
msg = '%s is in state %s' % (unit_name, state) |
|
19.1.14
by Aaron Bentley
More error handling fixes. |
98 |
Exception.__init__(self, msg) |
771.2.1
by Aaron Bentley
Increase set of agent-state-infos that indicate failure. |
99 |
self.unit_name = unit_name |
100 |
self.state = state |
|
2
by Aaron Bentley
Added initial deploy_stack. |
101 |
|
102 |
||
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
103 |
class BootstrapMismatch(Exception): |
104 |
||
105 |
def __init__(self, arg_name, arg_val, env_name, env_val): |
|
106 |
super(BootstrapMismatch, self).__init__( |
|
107 |
'--{} {} does not match {}: {}'.format( |
|
108 |
arg_name, arg_val, env_name, env_val)) |
|
109 |
||
110 |
||
1258.2.5
by Curtis Hovey
Added upgrade_mongo to EnvJujuClient. |
111 |
class UpgradeMongoNotSupported(Exception): |
112 |
||
113 |
def __init__(self): |
|
114 |
super(UpgradeMongoNotSupported, self).__init__( |
|
115 |
'This client does not support upgrade-mongo') |
|
116 |
||
117 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
118 |
class JESNotSupported(Exception): |
119 |
||
120 |
def __init__(self): |
|
121 |
super(JESNotSupported, self).__init__( |
|
1183.1.9
by Curtis Hovey
Revert change that makes diff hard to read. |
122 |
'This client does not support JES') |
1044.1.9
by Aaron Bentley
Update and add tests. |
123 |
|
124 |
||
125 |
class JESByDefault(Exception): |
|
126 |
||
127 |
def __init__(self): |
|
1044.1.13
by Aaron Bentley
Add more enable_jes tests. |
128 |
super(JESByDefault, self).__init__( |
1183.1.9
by Curtis Hovey
Revert change that makes diff hard to read. |
129 |
'This client does not need to enable JES') |
1183.1.2
by Curtis Hovey
Save point. |
130 |
|
131 |
||
1315.2.1
by Aaron Bentley
Use machine id instead of machine number. |
132 |
Machine = namedtuple('Machine', ['machine_id', 'info']) |
1306.1.7
by Curtis Hovey
Added get_controller_leader. |
133 |
|
134 |
||
19.1.18
by Aaron Bentley
Provide destroy-environment script to simplify code. |
135 |
def yaml_loads(yaml_str): |
136 |
return yaml.safe_load(StringIO(yaml_str)) |
|
137 |
||
138 |
||
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
139 |
def coalesce_agent_status(agent_item): |
140 |
"""Return the machine agent-state or the unit agent-status."""
|
|
141 |
state = agent_item.get('agent-state') |
|
142 |
if state is None and agent_item.get('agent-status') is not None: |
|
143 |
state = agent_item.get('agent-status').get('current') |
|
1293
by Curtis Hovey
Support juju-status. |
144 |
if state is None and agent_item.get('juju-status') is not None: |
145 |
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. |
146 |
if state is None: |
147 |
state = 'no-agent' |
|
148 |
return state |
|
149 |
||
150 |
||
953.3.7
by Nate Finch
update for code review comments |
151 |
def make_client(juju_path, debug, env_name, temp_env_name): |
152 |
env = SimpleEnvironment.from_config(env_name) |
|
153 |
if temp_env_name is not None: |
|
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
154 |
env.set_model_name(temp_env_name) |
1169.3.1
by John George
Enforce full path to the juju binary it given, from add_basic_testing_arguments. |
155 |
return EnvJujuClient.by_version(env, juju_path, debug) |
953.3.7
by Nate Finch
update for code review comments |
156 |
|
157 |
||
185.1.1
by Aaron Bentley
Fix cloud test handling of 'Unable to connect to environment.' |
158 |
class CannotConnectEnv(subprocess.CalledProcessError): |
159 |
||
160 |
def __init__(self, e): |
|
161 |
super(CannotConnectEnv, self).__init__(e.returncode, e.cmd, e.output) |
|
162 |
||
163 |
||
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
164 |
class StatusNotMet(Exception): |
165 |
||
166 |
_fmt = 'Expected status not reached in {env}.' |
|
167 |
||
168 |
def __init__(self, environment_name, status): |
|
169 |
self.env = environment_name |
|
747.3.3
by Aaron Bentley
Implement retrying add-machine. |
170 |
self.status = status |
171 |
||
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
172 |
def __str__(self): |
173 |
return self._fmt.format(env=self.env) |
|
174 |
||
175 |
||
176 |
class AgentsNotStarted(StatusNotMet): |
|
177 |
||
178 |
_fmt = 'Timed out waiting for agents to start in {env}.' |
|
179 |
||
180 |
||
181 |
class VersionsNotUpdated(StatusNotMet): |
|
182 |
||
183 |
_fmt = 'Some versions did not update.' |
|
184 |
||
185 |
||
186 |
class WorkloadsNotReady(StatusNotMet): |
|
187 |
||
188 |
_fmt = 'Workloads not ready in {env}.' |
|
189 |
||
747.3.3
by Aaron Bentley
Implement retrying add-machine. |
190 |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
191 |
@contextmanager
|
192 |
def temp_yaml_file(yaml_dict): |
|
1272.1.1
by Aaron Bentley
temp_yaml_file provides the name of a closed file, for Windows. |
193 |
temp_file = NamedTemporaryFile(suffix='.yaml', delete=False) |
194 |
try: |
|
195 |
with temp_file: |
|
196 |
yaml.safe_dump(yaml_dict, temp_file) |
|
197 |
yield temp_file.name |
|
198 |
finally: |
|
199 |
os.unlink(temp_file.name) |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
200 |
|
201 |
||
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
202 |
class EnvJujuClient: |
203 |
||
1251.2.4
by Aaron Bentley
Rename bootstrap_supports => bootstrap_replaces |
204 |
# The environments.yaml options that are replaced by bootstrap options.
|
205 |
#
|
|
1242.3.8
by Aaron Bentley
Override default-series even when --bootstrap-series is supplied. |
206 |
# As described in bug #1538735, default-series and --bootstrap-series must
|
1251.2.5
by Aaron Bentley
Tweak verbiage. |
207 |
# match. 'default-series' should be here, but is omitted so that
|
208 |
# default-series is always forced to match --bootstrap-series.
|
|
1251.2.4
by Aaron Bentley
Rename bootstrap_supports => bootstrap_replaces |
209 |
bootstrap_replaces = frozenset(['agent-version']) |
1242.3.7
by Aaron Bentley
BootstrapManager support for bootstrap option. |
210 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
211 |
# What feature flags have existed that CI used.
|
212 |
known_feature_flags = frozenset([ |
|
213 |
'actions', 'jes', 'address-allocation', 'cloudsigma']) |
|
214 |
||
215 |
# What feature flags are used by this version of the juju client.
|
|
216 |
used_feature_flags = frozenset(['address-allocation']) |
|
217 |
||
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
218 |
_show_status = 'show-status' |
219 |
||
657.1.7
by Aaron Bentley
Clean-up. |
220 |
@classmethod
|
650.1.9
by Aaron Bentley
Merged trunk into compatibility-test. |
221 |
def get_version(cls, juju_path=None): |
650.1.1
by Aaron Bentley
Add juju_path to from_config |
222 |
if juju_path is None: |
223 |
juju_path = 'juju' |
|
224 |
return subprocess.check_output((juju_path, '--version')).strip() |
|
657.1.7
by Aaron Bentley
Clean-up. |
225 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
226 |
def enable_feature(self, flag): |
227 |
"""Enable juju feature by setting the given flag.
|
|
228 |
||
229 |
New versions of juju with the feature enabled by default will silently
|
|
230 |
allow this call, but will not export the environment variable.
|
|
231 |
"""
|
|
232 |
if flag not in self.known_feature_flags: |
|
233 |
raise ValueError('Unknown feature flag: %r' % (flag,)) |
|
234 |
self.feature_flags.add(flag) |
|
235 |
||
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
236 |
def get_jes_command(self): |
237 |
"""For Juju 2.0, this is always kill-controller."""
|
|
238 |
return KILL_CONTROLLER |
|
239 |
||
1145.2.11
by Curtis Hovey
Let the client set permanent based on client.is_jes_enabled. |
240 |
def is_jes_enabled(self): |
1145.2.2
by Curtis Hovey
Extracted get_jes_command from is_jes_enabled. |
241 |
"""Does the state-server support multiple environments."""
|
1145.2.6
by Curtis Hovey
Raise JESNotSupported from get_jes_command when jes is not supported. |
242 |
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. |
243 |
self.get_jes_command() |
1145.2.6
by Curtis Hovey
Raise JESNotSupported from get_jes_command when jes is not supported. |
244 |
return True |
245 |
except JESNotSupported: |
|
246 |
return False |
|
1145.2.2
by Curtis Hovey
Extracted get_jes_command from is_jes_enabled. |
247 |
|
1162.2.21
by Aaron Bentley
Implement and use tear_down(try_jes=True) |
248 |
def enable_jes(self): |
249 |
"""Enable JES if JES is optional.
|
|
250 |
||
251 |
Specifically implemented by the clients that optionally support JES.
|
|
252 |
This version raises either JESByDefault or JESNotSupported.
|
|
253 |
||
254 |
:raises: JESByDefault when JES is always enabled; Juju has the
|
|
255 |
'destroy-controller' command.
|
|
256 |
:raises: JESNotSupported when JES is not supported; Juju does not have
|
|
257 |
the 'system kill' command when the JES feature flag is set.
|
|
258 |
"""
|
|
259 |
if self.is_jes_enabled(): |
|
260 |
raise JESByDefault() |
|
261 |
else: |
|
262 |
raise JESNotSupported() |
|
263 |
||
657.1.7
by Aaron Bentley
Clean-up. |
264 |
@classmethod
|
265 |
def get_full_path(cls): |
|
161
by Curtis Hovey
Windows and py3 compatability. |
266 |
if sys.platform == 'win32': |
163
by Curtis Hovey
Extracted the windows command incase it needs to be reused. |
267 |
return WIN_JUJU_CMD |
107.1.2
by Aaron Bentley
Use full path when running under sudo. |
268 |
return subprocess.check_output(('which', 'juju')).rstrip('\n') |
19.1.18
by Aaron Bentley
Provide destroy-environment script to simplify code. |
269 |
|
657.1.2
by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel. |
270 |
@classmethod
|
663.1.7
by Aaron Bentley
Add --upload-tools to assess-heterogeneous-control. |
271 |
def by_version(cls, env, juju_path=None, debug=False): |
650.1.1
by Aaron Bentley
Add juju_path to from_config |
272 |
version = cls.get_version(juju_path) |
273 |
if juju_path is None: |
|
274 |
full_path = cls.get_full_path() |
|
275 |
else: |
|
276 |
full_path = os.path.abspath(juju_path) |
|
657.1.2
by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel. |
277 |
if version.startswith('1.16'): |
278 |
raise Exception('Unsupported juju: %s' % version) |
|
953.3.9
by Nate Finch
more code review changes |
279 |
elif re.match('^1\.22[.-]', version): |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
280 |
client_class = EnvJujuClient22 |
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
281 |
elif re.match('^1\.24[.-]', version): |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
282 |
client_class = EnvJujuClient24 |
957.2.1
by Aaron Bentley
Treat cloudsigma as provisional in 2.5 |
283 |
elif re.match('^1\.25[.-]', version): |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
284 |
client_class = EnvJujuClient25 |
1074.2.1
by Aaron Bentley
Add EnvJujuClient support for Juju 2.6. |
285 |
elif re.match('^1\.26[.-]', version): |
286 |
client_class = EnvJujuClient26 |
|
1221.1.16
by Aaron Bentley
by_version uses 1X for 1.X-series clients. |
287 |
elif re.match('^1\.', version): |
288 |
client_class = EnvJujuClient1X |
|
1254.1.1
by Aaron Bentley
Use EnvJujuClient for 2.0-alpha2. |
289 |
elif re.match('^2\.0-alpha1', version): |
1221.1.17
by Aaron Bentley
Provide for a separate 2.0-alpha1 client. |
290 |
client_class = EnvJujuClient2A1 |
1283.2.1
by Aaron Bentley
Expect cloud-credentials behaviour from beta1. |
291 |
elif re.match('^2\.0-alpha2', version): |
1258.1.3
by Aaron Bentley
Support transitional jujus. |
292 |
client_class = EnvJujuClient2A2 |
1331
by Aaron Bentley
New behaviour for beta3, not beta2. |
293 |
elif re.match('^2\.0-(alpha3|beta[12])', version): |
1330
by Aaron Bentley
Use EnvJujuClient for beta2. |
294 |
client_class = EnvJujuClient2B2 |
295 |
else: |
|
1283.2.1
by Aaron Bentley
Expect cloud-credentials behaviour from beta1. |
296 |
client_class = EnvJujuClient |
1201.2.13
by Aaron Bentley
Fix juju_home handling in by_version. |
297 |
return client_class(env, version, full_path, debug=debug) |
657.1.2
by Aaron Bentley
Add EnvJujuClient tests from JujuClientDevel. |
298 |
|
1221.1.9
by Aaron Bentley
Further extend clone(). |
299 |
def clone(self, env=None, version=None, full_path=None, debug=None, |
300 |
cls=None): |
|
301 |
"""Create a clone of this EnvJujuClient.
|
|
302 |
||
303 |
By default, the class, environment, version, full_path, and debug
|
|
304 |
settings will match the original, but each can be overridden.
|
|
305 |
"""
|
|
1221.1.8
by Aaron Bentley
Implement EnvJujuClient.clone. |
306 |
if env is None: |
307 |
env = self.env |
|
308 |
if version is None: |
|
309 |
version = self.version |
|
310 |
if full_path is None: |
|
311 |
full_path = self.full_path |
|
312 |
if debug is None: |
|
313 |
debug = self.debug |
|
1221.1.9
by Aaron Bentley
Further extend clone(). |
314 |
if cls is None: |
315 |
cls = self.__class__ |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
316 |
other = cls(env, version, full_path, debug=debug) |
317 |
other.feature_flags.update( |
|
318 |
self.feature_flags.intersection(other.used_feature_flags)) |
|
319 |
return other |
|
1221.1.8
by Aaron Bentley
Implement EnvJujuClient.clone. |
320 |
|
1240.2.1
by Aaron Bentley
Support models/cache.yaml |
321 |
def get_cache_path(self): |
322 |
return get_cache_path(self.env.juju_home, models=True) |
|
323 |
||
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
324 |
def _full_args(self, command, sudo, args, |
325 |
timeout=None, include_e=True, admin=False): |
|
196
by Curtis Hovey
Reverted most of the hacks for the juju 1.17.1 tests. Kept some test fixes. |
326 |
# sudo is not needed for devel releases.
|
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
327 |
if admin: |
1306.1.25
by Curtis Hovey
Alaway ask for admin when waiting for ha. |
328 |
e_arg = ('-m', self.get_admin_model_name()) |
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
329 |
elif self.env is None or not include_e: |
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
330 |
e_arg = () |
331 |
else: |
|
1221.1.19
by Aaron Bentley
Tweaked enough to bootstrap. |
332 |
e_arg = ('-m', self.env.environment) |
1069.1.1
by Aaron Bentley
Replace timeout utility in _full_args. |
333 |
if timeout is None: |
372.1.2
by Aaron Bentley
Support supplying timeout to calls. |
334 |
prefix = () |
335 |
else: |
|
1069.1.1
by Aaron Bentley
Replace timeout utility in _full_args. |
336 |
prefix = get_timeout_prefix(timeout, self._timeout_path) |
471.1.3
by Aaron Bentley
Support debug flag. |
337 |
logging = '--debug' if self.debug else '--show-log' |
953.3.13
by Nate Finch
more code review changes |
338 |
|
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
339 |
# If args is a string, make it a tuple. This makes writing commands
|
340 |
# with one argument a bit nicer.
|
|
1091.3.2
by James Tunnicliffe
clean_environment bails early if there is no environment to clean. |
341 |
if isinstance(args, basestring): |
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
342 |
args = (args,) |
953.3.13
by Nate Finch
more code review changes |
343 |
# we split the command here so that the caller can control where the -e
|
344 |
# <env> flag goes. Everything in the command string is put before the
|
|
345 |
# -e flag.
|
|
953.3.11
by Nate Finch
set longer timeout for fill log actions, and fix -e testing problem |
346 |
command = command.split() |
966.1.6
by John George
Non-action specific handling of juju sub-commands. |
347 |
return prefix + ('juju', logging,) + tuple(command) + e_arg + args |
19.1.17
by Aaron Bentley
Isolate client to support incompatible command line changes. |
348 |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
349 |
@staticmethod
|
350 |
def _get_env(env): |
|
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
351 |
if not isinstance(env, JujuData) and isinstance(env, |
352 |
SimpleEnvironment): |
|
1260.2.23
by Aaron Bentley
Test SimpleEnvironment/JujuData on init. |
353 |
# FIXME: JujuData should be used from the start.
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
354 |
env = JujuData.from_env(env) |
355 |
return env |
|
356 |
||
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
357 |
def __init__(self, env, version, full_path, juju_home=None, debug=False): |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
358 |
self.env = self._get_env(env) |
657.1.7
by Aaron Bentley
Clean-up. |
359 |
self.version = version |
360 |
self.full_path = full_path |
|
361 |
self.debug = debug |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
362 |
self.feature_flags = set() |
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
363 |
if env is not None: |
364 |
if juju_home is None: |
|
365 |
if env.juju_home is None: |
|
366 |
env.juju_home = get_juju_home() |
|
367 |
else: |
|
368 |
env.juju_home = juju_home |
|
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
369 |
self.juju_timings = {} |
1069.1.1
by Aaron Bentley
Replace timeout utility in _full_args. |
370 |
self._timeout_path = get_timeout_path() |
657.1.7
by Aaron Bentley
Clean-up. |
371 |
|
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
372 |
def _shell_environ(self): |
653
by Aaron Bentley
Add assess-bootstrap. |
373 |
"""Generate a suitable shell environment.
|
374 |
||
375 |
Juju's directory must be in the PATH to support plugins.
|
|
376 |
"""
|
|
652
by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery |
377 |
env = dict(os.environ) |
378 |
if self.full_path is not None: |
|
1080.1.12
by Aaron Bentley
Use os.pathsep in PATH. |
379 |
env['PATH'] = '{}{}{}'.format(os.path.dirname(self.full_path), |
380 |
os.pathsep, env['PATH']) |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
381 |
flags = self.feature_flags.intersection(self.used_feature_flags) |
382 |
if flags: |
|
383 |
env[JUJU_DEV_FEATURE_FLAGS] = ','.join(sorted(flags)) |
|
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
384 |
env['JUJU_DATA'] = self.env.juju_home |
652
by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery |
385 |
return env |
386 |
||
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
387 |
def add_ssh_machines(self, machines): |
1256.1.1
by Aaron Bentley
Retry initial failed add-machine if needed. |
388 |
for count, machine in enumerate(machines): |
389 |
try: |
|
390 |
self.juju('add-machine', ('ssh:' + machine,)) |
|
391 |
except subprocess.CalledProcessError: |
|
392 |
if count != 0: |
|
393 |
raise
|
|
394 |
logging.warning('add-machine failed. Will retry.') |
|
395 |
pause(30) |
|
396 |
self.juju('add-machine', ('ssh:' + machine,)) |
|
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
397 |
|
1260.2.5
by Aaron Bentley
Split up cloud and region determination. |
398 |
@staticmethod
|
399 |
def get_cloud_region(cloud, region): |
|
1260.2.4
by Aaron Bentley
Implement openstack + maas support. |
400 |
if region is None: |
401 |
return cloud |
|
402 |
return '{}/{}'.format(cloud, region) |
|
403 |
||
404 |
def get_bootstrap_args(self, upload_tools, config_filename, |
|
405 |
bootstrap_series=None): |
|
406 |
"""Return the bootstrap arguments for the substrate."""
|
|
407 |
if self.env.maas: |
|
408 |
constraints = 'mem=2G arch=amd64' |
|
409 |
elif self.env.joyent: |
|
410 |
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
|
|
411 |
constraints = 'mem=2G cpu-cores=1' |
|
412 |
else: |
|
413 |
constraints = 'mem=2G' |
|
1260.2.24
by Aaron Bentley
Move cloud/location to JujuData, add tests, fix lint. |
414 |
cloud_region = self.get_cloud_region(self.env.get_cloud(), |
415 |
self.env.get_region()) |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
416 |
args = ['--constraints', constraints, self.env.environment, |
1315.2.2
by Aaron Bentley
Use --default-model in bootstrap. |
417 |
cloud_region, '--config', config_filename, |
418 |
'--default-model', self.env.environment] |
|
663.1.6
by Aaron Bentley
Base support for upload-tools |
419 |
if upload_tools: |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
420 |
args.insert(0, '--upload-tools') |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
421 |
else: |
422 |
args.extend(['--agent-version', self.get_matching_agent_version()]) |
|
423 |
||
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
424 |
if bootstrap_series is not None: |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
425 |
args.extend(['--bootstrap-series', bootstrap_series]) |
426 |
return tuple(args) |
|
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
427 |
|
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
428 |
@contextmanager
|
429 |
def _bootstrap_config(self): |
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
430 |
config_dict = make_safe_config(self) |
431 |
# Strip unneeded variables.
|
|
1260.2.8
by Aaron Bentley
Fix lint. |
432 |
config_dict = dict((k, v) for k, v in config_dict.items() if k not in { |
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
433 |
'access-key', |
1325.1.1
by Aaron Bentley
Exclude admin-secret from configs. |
434 |
'admin-secret', |
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
435 |
'application-id', |
436 |
'application-password', |
|
437 |
'auth-url', |
|
1280.1.1
by Aaron Bentley
Supply bootstrap host as manual region. |
438 |
'bootstrap-host', |
1260.2.26
by Aaron Bentley
Test _bootstrap_config. |
439 |
'client-email', |
440 |
'client-id', |
|
441 |
'control-bucket', |
|
442 |
'location', |
|
443 |
'maas-oauth', |
|
444 |
'maas-server', |
|
445 |
'manta-key-id', |
|
446 |
'manta-user', |
|
447 |
'name', |
|
448 |
'password', |
|
449 |
'private-key', |
|
450 |
'region', |
|
451 |
'sdc-key-id', |
|
452 |
'sdc-url', |
|
453 |
'sdc-user', |
|
454 |
'secret-key', |
|
455 |
'storage-account-name', |
|
456 |
'subscription-id', |
|
457 |
'tenant-id', |
|
458 |
'tenant-name', |
|
459 |
'type', |
|
460 |
'username', |
|
1306.1.1
by Curtis Hovey
Save spike to get leader and followers. |
461 |
})
|
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
462 |
with temp_yaml_file(config_dict) as config_filename: |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
463 |
yield config_filename |
464 |
||
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
465 |
def _check_bootstrap(self): |
466 |
if self.env.environment != self.env.controller.name: |
|
467 |
raise AssertionError( |
|
468 |
'Controller and environment names should not vary (yet)') |
|
469 |
||
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
470 |
def bootstrap(self, upload_tools=False, bootstrap_series=None): |
471 |
"""Bootstrap a controller."""
|
|
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
472 |
self._check_bootstrap() |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
473 |
with self._bootstrap_config() as config_filename: |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
474 |
args = self.get_bootstrap_args( |
475 |
upload_tools, config_filename, bootstrap_series) |
|
476 |
self.juju('bootstrap', args, include_e=False) |
|
477 |
||
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
478 |
@contextmanager
|
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
479 |
def bootstrap_async(self, upload_tools=False, bootstrap_series=None): |
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
480 |
self._check_bootstrap() |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
481 |
with self._bootstrap_config() as config_filename: |
482 |
args = self.get_bootstrap_args( |
|
483 |
upload_tools, config_filename, bootstrap_series) |
|
1260.2.20
by Aaron Bentley
Get all tests passing. |
484 |
with self.juju_async('bootstrap', args, include_e=False): |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
485 |
yield
|
486 |
log.info('Waiting for bootstrap of {}.'.format( |
|
487 |
self.env.environment)) |
|
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
488 |
|
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
489 |
def create_environment(self, controller_client, config_file): |
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
490 |
controller_client.controller_juju('create-model', ( |
491 |
self.env.environment, '--config', config_file)) |
|
1162.2.2
by Aaron Bentley
Switch to direct BootstrapManager. |
492 |
|
1221.1.15
by Aaron Bentley
Implement and use destroy_model for destroying models. |
493 |
def destroy_model(self): |
1210.1.1
by Aaron Bentley
Destroy environment without --force if possible. |
494 |
exit_status = self.juju( |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
495 |
'destroy-model', (self.env.environment, '-y',), |
1221.1.15
by Aaron Bentley
Implement and use destroy_model for destroying models. |
496 |
include_e=False, timeout=timedelta(minutes=10).total_seconds()) |
1210.1.1
by Aaron Bentley
Destroy environment without --force if possible. |
497 |
return exit_status |
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
498 |
|
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
499 |
def kill_controller(self): |
500 |
"""Kill a controller and its environments."""
|
|
501 |
seen_cmd = self.get_jes_command() |
|
502 |
self.juju( |
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
503 |
_jes_cmds[seen_cmd]['kill'], (self.env.controller.name, '-y'), |
1162.1.1
by Aaron Bentley
Abstract jes support, make enable_jes optional in assess_jes. |
504 |
include_e=False, check=False, timeout=600) |
505 |
||
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
506 |
def get_juju_output(self, command, *args, **kwargs): |
953.3.13
by Nate Finch
more code review changes |
507 |
"""Call a juju command and return the output.
|
508 |
||
509 |
Sub process will be called as 'juju <command> <args> <kwargs>'. Note
|
|
510 |
that <command> may be a space delimited list of arguments. The -e
|
|
953.3.15
by Aaron Bentley
Fix lint. |
511 |
<environment> flag will be placed after <command> and before args.
|
953.3.13
by Nate Finch
more code review changes |
512 |
"""
|
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
513 |
args = self._full_args(command, False, args, |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
514 |
timeout=kwargs.get('timeout'), |
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
515 |
include_e=kwargs.get('include_e', True), |
516 |
admin=kwargs.get('admin', False)) |
|
652
by Aaron Bentley
Insert path-to-juju in PATH, fix test_assess_recovery |
517 |
env = self._shell_environ() |
1091.4.1
by James Tunnicliffe
Merged upstream |
518 |
log.debug(args) |
519 |
# Mutate os.environ instead of supplying env parameter so
|
|
520 |
# Windows can search env['PATH']
|
|
521 |
with scoped_environ(env): |
|
522 |
proc = subprocess.Popen( |
|
523 |
args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, |
|
524 |
stderr=subprocess.PIPE) |
|
525 |
sub_output, sub_error = proc.communicate() |
|
526 |
log.debug(sub_output) |
|
527 |
if proc.returncode != 0: |
|
528 |
log.debug(sub_error) |
|
529 |
e = subprocess.CalledProcessError( |
|
1326.1.1
by Aaron Bentley
Include all arguments in exception. |
530 |
proc.returncode, args, sub_output) |
1091.4.1
by James Tunnicliffe
Merged upstream |
531 |
e.stderr = sub_error |
532 |
if ( |
|
533 |
'Unable to connect to environment' in sub_error or |
|
534 |
'MissingOrIncorrectVersionHeader' in sub_error or |
|
535 |
'307: Temporary Redirect' in sub_error): |
|
218
by Curtis Hovey
Convert MissingOrIncorrectVersionHeader to CannotConnectEnv. |
536 |
raise CannotConnectEnv(e) |
1091.4.1
by James Tunnicliffe
Merged upstream |
537 |
raise e |
538 |
return sub_output |
|
19.1.18
by Aaron Bentley
Provide destroy-environment script to simplify code. |
539 |
|
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
540 |
def show_status(self): |
541 |
"""Print the status to output."""
|
|
1246.1.4
by Curtis Hovey
Safe print yaml status to ensure continuity. |
542 |
self.juju(self._show_status, ('--format', 'yaml')) |
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
543 |
|
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
544 |
def get_status(self, timeout=60, raw=False, admin=False, *args): |
19.1.17
by Aaron Bentley
Isolate client to support incompatible command line changes. |
545 |
"""Get the current status as a dict."""
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
546 |
# 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. |
547 |
for ignored in until_timeout(timeout): |
331.1.1
by Aaron Bentley
juju status retries on error for 30 seconds. |
548 |
try: |
979.3.5
by Horacio Durán
Addressed curtis observations. |
549 |
if raw: |
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
550 |
return self.get_juju_output(self._show_status, *args) |
551 |
return Status.from_text( |
|
1246.1.1
by Curtis Hovey
Call status with --format yaml. |
552 |
self.get_juju_output( |
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
553 |
self._show_status, '--format', 'yaml', admin=admin)) |
1091.5.4
by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors. |
554 |
except subprocess.CalledProcessError: |
331.1.1
by Aaron Bentley
juju status retries on error for 30 seconds. |
555 |
pass
|
556 |
raise Exception( |
|
1091.5.4
by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors. |
557 |
'Timed out waiting for juju status to succeed') |
19.1.17
by Aaron Bentley
Isolate client to support incompatible command line changes. |
558 |
|
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
559 |
@staticmethod
|
560 |
def _dict_as_option_strings(options): |
|
561 |
return tuple('{}={}'.format(*item) for item in options.items()) |
|
562 |
||
1239.1.1
by Aaron Bentley
Implement and use EnvJujuClient.set_config. |
563 |
def set_config(self, service, options): |
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
564 |
option_strings = self._dict_as_option_strings(options) |
565 |
self.juju('set-config', (service,) + option_strings) |
|
1239.1.1
by Aaron Bentley
Implement and use EnvJujuClient.set_config. |
566 |
|
1221.5.11
by Aaron Bentley
Extract EnvJujuClient.get_config. |
567 |
def get_config(self, service): |
1221.5.12
by Aaron Bentley
Support get-config. |
568 |
return yaml_loads(self.get_juju_output('get-config', service)) |
1221.5.11
by Aaron Bentley
Extract EnvJujuClient.get_config. |
569 |
|
979.2.1
by John George
Add support for checking chaos is complete, from run_chaos_monkey.py |
570 |
def get_service_config(self, service, timeout=60): |
571 |
for ignored in until_timeout(timeout): |
|
572 |
try: |
|
1221.5.11
by Aaron Bentley
Extract EnvJujuClient.get_config. |
573 |
return self.get_config(service) |
979.2.1
by John George
Add support for checking chaos is complete, from run_chaos_monkey.py |
574 |
except subprocess.CalledProcessError: |
575 |
pass
|
|
576 |
raise Exception( |
|
577 |
'Timed out waiting for juju get %s' % (service)) |
|
578 |
||
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
579 |
def set_model_constraints(self, constraints): |
580 |
constraint_strings = self._dict_as_option_strings(constraints) |
|
581 |
return self.juju('set-model-constraints', constraint_strings) |
|
582 |
||
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
583 |
def get_model_config(self): |
584 |
"""Return the value of the environment's configured option."""
|
|
585 |
return yaml.safe_load(self.get_juju_output('get-model-config')) |
|
586 |
||
657.1.7
by Aaron Bentley
Clean-up. |
587 |
def get_env_option(self, option): |
588 |
"""Return the value of the environment's configured option."""
|
|
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
589 |
return self.get_juju_output('get-model-config', option) |
657.1.7
by Aaron Bentley
Clean-up. |
590 |
|
591 |
def set_env_option(self, option, value): |
|
592 |
"""Set the value of the option in the environment."""
|
|
593 |
option_value = "%s=%s" % (option, value) |
|
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
594 |
return self.juju('set-model-config', (option_value,)) |
657.1.7
by Aaron Bentley
Clean-up. |
595 |
|
880.1.17
by Aaron Bentley
Fake merge of trunk into no-environment. |
596 |
def set_testing_tools_metadata_url(self): |
597 |
url = self.get_env_option('tools-metadata-url') |
|
598 |
if 'testing' not in url: |
|
599 |
testing_url = url.replace('/tools', '/testing/tools') |
|
600 |
self.set_env_option('tools-metadata-url', testing_url) |
|
601 |
||
768.1.1
by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment. |
602 |
def juju(self, command, args, sudo=False, check=True, include_e=True, |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
603 |
timeout=None, extra_env=None): |
657.1.7
by Aaron Bentley
Clean-up. |
604 |
"""Run a command under juju for the current environment."""
|
768.1.1
by Aaron Bentley
Add timeout to EnvJujuClient.destroy_environment. |
605 |
args = self._full_args(command, sudo, args, include_e=include_e, |
606 |
timeout=timeout) |
|
1091.4.1
by James Tunnicliffe
Merged upstream |
607 |
log.info(' '.join(args)) |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
608 |
env = self._shell_environ() |
966.2.1
by Curtis Hovey
Added extra_env, but tests fail. |
609 |
if extra_env is not None: |
610 |
env.update(extra_env) |
|
657.1.7
by Aaron Bentley
Clean-up. |
611 |
if check: |
1044.1.9
by Aaron Bentley
Update and add tests. |
612 |
call_func = subprocess.check_call |
613 |
else: |
|
614 |
call_func = subprocess.call |
|
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
615 |
start_time = time.time() |
1080.1.14
by Aaron Bentley
Add docs. |
616 |
# Mutate os.environ instead of supplying env parameter so Windows can
|
617 |
# search env['PATH']
|
|
1080.1.10
by Aaron Bentley
Supply the shell environment via os.environ. |
618 |
with scoped_environ(env): |
1080.1.11
by Aaron Bentley
Stop supplying env to subprocess calls. |
619 |
rval = call_func(args) |
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
620 |
self.juju_timings.setdefault(args, []).append( |
621 |
(time.time() - start_time)) |
|
622 |
return rval |
|
623 |
||
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
624 |
def controller_juju(self, command, args): |
625 |
args = ('-c', self.env.controller.name) + args |
|
626 |
return self.juju(command, args, include_e=False) |
|
627 |
||
1048.2.1
by John George
Capture duration of juju commands from the client perspective. |
628 |
def get_juju_timings(self): |
629 |
stringified_timings = {} |
|
630 |
for command, timings in self.juju_timings.items(): |
|
631 |
stringified_timings[' '.join(command)] = timings |
|
632 |
return stringified_timings |
|
657.1.7
by Aaron Bentley
Clean-up. |
633 |
|
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
634 |
@contextmanager
|
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
635 |
def juju_async(self, command, args, include_e=True, timeout=None): |
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
636 |
full_args = self._full_args(command, False, args, include_e=include_e, |
637 |
timeout=timeout) |
|
1091.4.1
by James Tunnicliffe
Merged upstream |
638 |
log.info(' '.join(args)) |
1044.1.6
by Aaron Bentley
Use per-client JUJU_HOME. |
639 |
env = self._shell_environ() |
1080.1.14
by Aaron Bentley
Add docs. |
640 |
# Mutate os.environ instead of supplying env parameter so Windows can
|
641 |
# search env['PATH']
|
|
1080.1.10
by Aaron Bentley
Supply the shell environment via os.environ. |
642 |
with scoped_environ(env): |
1080.1.11
by Aaron Bentley
Stop supplying env to subprocess calls. |
643 |
proc = subprocess.Popen(full_args) |
807.1.3
by Aaron Bentley
Get juju_async under test. |
644 |
yield proc |
807.1.2
by Aaron Bentley
Use async bootstraps for industrial testing. |
645 |
retcode = proc.wait() |
646 |
if retcode != 0: |
|
647 |
raise subprocess.CalledProcessError(retcode, full_args) |
|
648 |
||
1145.3.3
by John George
Allow a service name to be passed when calling juju deploy. |
649 |
def deploy(self, charm, repository=None, to=None, service=None): |
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
650 |
args = [charm] |
651 |
if repository is not None: |
|
652 |
args.extend(['--repository', repository]) |
|
1014.2.1
by John George
background_chaos WIP |
653 |
if to is not None: |
654 |
args.extend(['--to', to]) |
|
1145.3.3
by John George
Allow a service name to be passed when calling juju deploy. |
655 |
if service is not None: |
656 |
args.extend([service]) |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
657 |
return self.juju('deploy', tuple(args)) |
650.1.12
by Aaron Bentley
Rename to assess-foreign, update to use EnvJujuClient. |
658 |
|
1221.4.1
by Aaron Bentley
Extract destroy-service to a method. |
659 |
def remove_service(self, service): |
1221.4.2
by Aaron Bentley
Introduce EnvJujuClient.remove_service. |
660 |
self.juju('remove-service', (service,)) |
1221.4.1
by Aaron Bentley
Extract destroy-service to a method. |
661 |
|
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
662 |
def deploy_bundle(self, bundle, timeout=_DEFAULT_BUNDLE_TIMEOUT): |
663 |
"""Deploy bundle using native juju 2.0 deploy command."""
|
|
664 |
self.juju('deploy', bundle, timeout=timeout) |
|
665 |
||
1204.1.2
by John George
Pass deployer timeout values as function parameters. |
666 |
def deployer(self, bundle, name=None, deploy_delay=10, timeout=3600): |
884
by John George
Add juju-deployer test support. |
667 |
"""deployer, using sudo if necessary."""
|
668 |
args = ( |
|
669 |
'--debug', |
|
1204.1.2
by John George
Pass deployer timeout values as function parameters. |
670 |
'--deploy-delay', str(deploy_delay), |
671 |
'--timeout', str(timeout), |
|
884
by John George
Add juju-deployer test support. |
672 |
'--config', bundle, |
673 |
)
|
|
953.2.1
by John George
Support taking a bundle name in addition to the bundle config file path. |
674 |
if name: |
675 |
args += (name,) |
|
884
by John George
Add juju-deployer test support. |
676 |
self.juju('deployer', args, self.env.needs_sudo()) |
677 |
||
1260.2.30
by Aaron Bentley
Updates from review |
678 |
def _get_substrate_constraints(self): |
679 |
if self.env.maas: |
|
680 |
return 'mem=2G arch=amd64' |
|
681 |
elif self.env.joyent: |
|
682 |
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
|
|
683 |
return 'mem=2G cpu-cores=1' |
|
684 |
else: |
|
685 |
return 'mem=2G' |
|
686 |
||
796.2.2
by John George
Call quickstart from EnvJujuClient, with constraints |
687 |
def quickstart(self, bundle, upload_tools=False): |
688 |
"""quickstart, using sudo if necessary."""
|
|
689 |
if self.env.maas: |
|
690 |
constraints = 'mem=2G arch=amd64' |
|
691 |
else: |
|
692 |
constraints = 'mem=2G' |
|
693 |
args = ('--constraints', constraints) |
|
694 |
if upload_tools: |
|
695 |
args = ('--upload-tools',) + args |
|
696 |
args = args + ('--no-browser', bundle,) |
|
966.2.2
by Curtis Hovey
pass extra_env={'JUJU': None} with the quickstart command. |
697 |
self.juju('quickstart', args, self.env.needs_sudo(), |
698 |
extra_env={'JUJU': self.full_path}) |
|
796.2.2
by John George
Call quickstart from EnvJujuClient, with constraints |
699 |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
700 |
def status_until(self, timeout, start=None): |
874.2.8
by Aaron Bentley
Add docs, simplify get_unit. |
701 |
"""Call and yield status until the timeout is reached.
|
702 |
||
703 |
Status will always be yielded once before checking the timeout.
|
|
704 |
||
705 |
This is intended for implementing things like wait_for_started.
|
|
706 |
||
707 |
:param timeout: The number of seconds to wait before timing out.
|
|
708 |
:param start: If supplied, the time to count from when determining
|
|
709 |
timeout.
|
|
710 |
"""
|
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
711 |
yield self.get_status() |
712 |
for remaining in until_timeout(timeout, start=start): |
|
713 |
yield self.get_status() |
|
714 |
||
1196.1.6
by Martin Packman
Changes for review by abentley |
715 |
def _wait_for_status(self, reporter, translate, exc_type=StatusNotMet, |
1196.1.4
by Martin Packman
Finish increased sanity on deployer tests |
716 |
timeout=1200, start=None): |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
717 |
"""Wait till status reaches an expected state with pretty reporting.
|
718 |
||
719 |
Always tries to get status at least once. Each status call has an
|
|
720 |
internal timeout of 60 seconds. This is independent of the timeout for
|
|
721 |
the whole wait, note this means this function may be overrun.
|
|
722 |
||
723 |
:param reporter: A GroupReporter instance for output.
|
|
724 |
:param translate: A callable that takes status to make states dict.
|
|
1196.1.6
by Martin Packman
Changes for review by abentley |
725 |
: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 |
726 |
:param timeout: Optional number of seconds to wait before timing out.
|
727 |
:param start: Optional time to count from when determining timeout.
|
|
728 |
"""
|
|
729 |
status = None |
|
730 |
try: |
|
731 |
for _ in chain([None], until_timeout(timeout, start=start)): |
|
732 |
try: |
|
733 |
status = self.get_status() |
|
734 |
except CannotConnectEnv: |
|
735 |
log.info('Suppressing "Unable to connect to environment"') |
|
736 |
continue
|
|
737 |
states = translate(status) |
|
738 |
if states is None: |
|
739 |
break
|
|
740 |
reporter.update(states) |
|
741 |
else: |
|
742 |
if status is not None: |
|
743 |
log.error(status.status_text) |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
744 |
raise exc_type(self.env.environment, status) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
745 |
finally: |
746 |
reporter.finish() |
|
747 |
return status |
|
748 |
||
751.1.1
by Aaron Bentley
Give equal time to old and new clients in DeployManyAttempt |
749 |
def wait_for_started(self, timeout=1200, start=None): |
657.1.5
by Aaron Bentley
Move wait_for_started to EnvJujuClient. |
750 |
"""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 |
751 |
reporter = GroupReporter(sys.stdout, 'started') |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
752 |
return self._wait_for_status( |
753 |
reporter, Status.check_agents_started, AgentsNotStarted, |
|
754 |
timeout=timeout, start=start) |
|
657.1.5
by Aaron Bentley
Move wait_for_started to EnvJujuClient. |
755 |
|
966.1.10
by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units. |
756 |
def wait_for_subordinate_units(self, service, unit_prefix, timeout=1200, |
757 |
start=None): |
|
966.1.12
by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter. |
758 |
"""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. |
759 |
unit_prefix."""
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
760 |
def status_to_subordinate_states(status): |
761 |
service_unit_count = status.get_service_unit_count(service) |
|
762 |
subordinate_unit_count = 0 |
|
763 |
unit_states = defaultdict(list) |
|
764 |
for name, unit in status.service_subordinate_units(service): |
|
765 |
if name.startswith(unit_prefix + '/'): |
|
766 |
subordinate_unit_count += 1 |
|
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
767 |
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 |
768 |
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. |
769 |
set(unit_states.keys()).issubset(AGENTS_READY)): |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
770 |
return None |
771 |
return unit_states |
|
966.1.12
by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter. |
772 |
reporter = GroupReporter(sys.stdout, 'started') |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
773 |
self._wait_for_status( |
774 |
reporter, status_to_subordinate_states, AgentsNotStarted, |
|
775 |
timeout=timeout, start=start) |
|
966.1.9
by John George
Add wait_for_subordinate_unit to jujupy.py. |
776 |
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
777 |
def wait_for_version(self, version, timeout=300, start=None): |
778 |
def status_to_version(status): |
|
779 |
versions = status.get_agent_versions() |
|
780 |
if versions.keys() == [version]: |
|
781 |
return None |
|
782 |
return versions |
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
783 |
reporter = GroupReporter(sys.stdout, version) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
784 |
self._wait_for_status(reporter, status_to_version, VersionsNotUpdated, |
1196.1.4
by Martin Packman
Finish increased sanity on deployer tests |
785 |
timeout=timeout, start=start) |
657.1.6
by Aaron Bentley
Move wait_for_version to EnvJujuClient. |
786 |
|
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
787 |
def list_models(self): |
788 |
"""List the models registered with the current controller."""
|
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
789 |
self.controller_juju('list-models', ()) |
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
790 |
|
791 |
def get_models(self): |
|
792 |
"""return a models dict with a 'models': [] key-value pair."""
|
|
793 |
output = self.get_juju_output( |
|
794 |
'list-models', '-c', self.env.environment, '--format', 'yaml', |
|
795 |
include_e=False) |
|
796 |
models = yaml_loads(output) |
|
797 |
return models |
|
798 |
||
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
799 |
def _get_models(self): |
800 |
"""return a list of model dicts."""
|
|
801 |
return self.get_models()['models'] |
|
802 |
||
803 |
def iter_model_clients(self): |
|
1318.2.1
by Aaron Bentley
Unify cloning code, add docs. |
804 |
"""Iterate through all the models that share this model's controller.
|
805 |
||
806 |
Works only if JES is enabled.
|
|
807 |
"""
|
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
808 |
models = self._get_models() |
809 |
if not models: |
|
810 |
yield self |
|
811 |
for model in models: |
|
1318.2.1
by Aaron Bentley
Unify cloning code, add docs. |
812 |
yield self._acquire_model_client(model['name']) |
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
813 |
|
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
814 |
def get_admin_model_name(self): |
815 |
"""Return the name of the 'admin' model.
|
|
816 |
||
817 |
Return the name of the environment when an 'admin' model does
|
|
818 |
not exist.
|
|
819 |
"""
|
|
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
820 |
return 'admin' |
821 |
||
1318.2.1
by Aaron Bentley
Unify cloning code, add docs. |
822 |
def _acquire_model_client(self, name): |
823 |
"""Get a client for a model with the supplied name.
|
|
824 |
||
825 |
If the name matches self, self is used. Otherwise, a clone is used.
|
|
826 |
"""
|
|
827 |
if name == self.env.environment: |
|
828 |
return self |
|
829 |
else: |
|
830 |
env = self.env.clone(model_name=name) |
|
831 |
return self.clone(env=env) |
|
832 |
||
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
833 |
def get_admin_client(self): |
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
834 |
"""Return a client for the admin model. May return self.
|
835 |
||
836 |
This may be inaccurate for models created using create_environment
|
|
837 |
rather than bootstrap.
|
|
838 |
"""
|
|
1318.2.1
by Aaron Bentley
Unify cloning code, add docs. |
839 |
return self._acquire_model_client(self.get_admin_model_name()) |
1306.1.21
by Curtis Hovey
added list-controllers and list-models support for 2.x. |
840 |
|
841 |
def list_controllers(self): |
|
842 |
"""List the controllers."""
|
|
843 |
self.juju('list-controllers', (), include_e=False) |
|
844 |
||
1306.1.4
by Curtis Hovey
Added get_controller_endpoint for show-controller. |
845 |
def get_controller_endpoint(self): |
846 |
"""Return the address of the controller leader."""
|
|
1324.1.1
by Aaron Bentley
Use controller name for show-controller. |
847 |
controller = self.env.controller.name |
1306.1.19
by Curtis Hovey
Fix calls to show-controller with include_e=False. |
848 |
output = self.get_juju_output( |
1324.1.1
by Aaron Bentley
Use controller name for show-controller. |
849 |
'show-controller', controller, include_e=False) |
1306.1.19
by Curtis Hovey
Fix calls to show-controller with include_e=False. |
850 |
info = yaml_loads(output) |
1324.1.1
by Aaron Bentley
Use controller name for show-controller. |
851 |
endpoint = info[controller]['details']['api-endpoints'][0] |
1310.1.2
by Curtis Hovey
Address ipv6 split address and port. |
852 |
address, port = split_address_port(endpoint) |
1306.1.4
by Curtis Hovey
Added get_controller_endpoint for show-controller. |
853 |
return address |
854 |
||
1306.1.6
by Curtis Hovey
Added get_controller_members. |
855 |
def get_controller_members(self): |
1306.1.8
by Curtis Hovey
Update docstrings. |
856 |
"""Return a list of Machines that are members of the controller.
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
857 |
|
1306.1.8
by Curtis Hovey
Update docstrings. |
858 |
The first machine in the list is the leader. the remaining machines
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
859 |
are followers in a HA relationship.
|
860 |
"""
|
|
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
861 |
members = [] |
1306.1.6
by Curtis Hovey
Added get_controller_members. |
862 |
status = self.get_status() |
1315.2.1
by Aaron Bentley
Use machine id instead of machine number. |
863 |
for machine_id, machine in status.iter_machines(): |
1306.1.6
by Curtis Hovey
Added get_controller_members. |
864 |
if self.get_controller_member_status(machine): |
1315.2.1
by Aaron Bentley
Use machine id instead of machine number. |
865 |
members.append(Machine(machine_id, machine)) |
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
866 |
if len(members) <= 1: |
867 |
return members |
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
868 |
# Search for the leader and make it the first in the list.
|
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
869 |
# If the endpoint address is not the same as the leader's dns_name,
|
870 |
# the members are return in the order they were discovered.
|
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
871 |
endpoint = self.get_controller_endpoint() |
1310.1.3
by Curtis Hovey
Address sorting comment from review. |
872 |
log.debug('Controller endpoint is at {}'.format(endpoint)) |
873 |
members.sort(key=lambda m: m.info.get('dns-name') != endpoint) |
|
874 |
return members |
|
1306.1.6
by Curtis Hovey
Added get_controller_members. |
875 |
|
1306.1.7
by Curtis Hovey
Added get_controller_leader. |
876 |
def get_controller_leader(self): |
1306.1.8
by Curtis Hovey
Update docstrings. |
877 |
"""Return the controller leader Machine."""
|
1306.1.7
by Curtis Hovey
Added get_controller_leader. |
878 |
controller_members = self.get_controller_members() |
879 |
return controller_members[0] |
|
880 |
||
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
881 |
@staticmethod
|
882 |
def get_controller_member_status(info_dict): |
|
1306.1.8
by Curtis Hovey
Update docstrings. |
883 |
"""Return the controller-member-status of the machine if it exists."""
|
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
884 |
return info_dict.get('controller-member-status') |
885 |
||
703.1.1
by Aaron Bentley
Add ensure-availability stage to industrial_test. |
886 |
def wait_for_ha(self, timeout=1200): |
887 |
desired_state = 'has-vote' |
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
888 |
reporter = GroupReporter(sys.stdout, desired_state) |
943.1.1
by Martin Packman
Use try/finally to always call finish on GroupReporter instances |
889 |
try: |
890 |
for remaining in until_timeout(timeout): |
|
1306.1.25
by Curtis Hovey
Alaway ask for admin when waiting for ha. |
891 |
status = self.get_status(admin=True) |
943.1.1
by Martin Packman
Use try/finally to always call finish on GroupReporter instances |
892 |
states = {} |
893 |
for machine, info in status.iter_machines(): |
|
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
894 |
status = self.get_controller_member_status(info) |
943.1.1
by Martin Packman
Use try/finally to always call finish on GroupReporter instances |
895 |
if status is None: |
896 |
continue
|
|
897 |
states.setdefault(status, []).append(machine) |
|
898 |
if states.keys() == [desired_state]: |
|
899 |
if len(states.get(desired_state, [])) >= 3: |
|
900 |
# XXX sinzui 2014-12-04: bug 1399277 happens because
|
|
901 |
# juju claims HA is ready when the monogo replica sets
|
|
902 |
# are not. Juju is not fully usable. The replica set
|
|
903 |
# lag might be 5 minutes.
|
|
904 |
pause(300) |
|
905 |
return
|
|
906 |
reporter.update(states) |
|
907 |
else: |
|
908 |
raise Exception('Timed out waiting for voting to be enabled.') |
|
909 |
finally: |
|
889.2.1
by Martin Packman
Make sure to finish GroupReporter instances even in error cases |
910 |
reporter.finish() |
703.1.1
by Aaron Bentley
Add ensure-availability stage to industrial_test. |
911 |
|
796.2.1
by John George
Add quickstart_deploy.py |
912 |
def wait_for_deploy_started(self, service_count=1, timeout=1200): |
913 |
"""Wait until service_count services are 'started'.
|
|
914 |
||
915 |
:param service_count: The number of services for which to wait.
|
|
916 |
:param timeout: The number of seconds to wait.
|
|
917 |
"""
|
|
918 |
for remaining in until_timeout(timeout): |
|
919 |
status = self.get_status() |
|
920 |
if status.get_service_count() >= service_count: |
|
921 |
return
|
|
922 |
else: |
|
923 |
raise Exception('Timed out waiting for services to start.') |
|
924 |
||
1215
by John George
Workload timeout increate to 10 minutes. |
925 |
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 |
926 |
"""Wait until all unit workloads are in a ready state."""
|
927 |
def status_to_workloads(status): |
|
928 |
unit_states = defaultdict(list) |
|
929 |
for name, unit in status.iter_units(): |
|
930 |
workload = unit.get('workload-status') |
|
931 |
if workload is not None: |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
932 |
state = workload['current'] |
1204.3.1
by Martin Packman
Fix bug introduced with unknown workloads and add test coverage |
933 |
else: |
934 |
state = 'unknown' |
|
935 |
unit_states[state].append(name) |
|
936 |
if set(('active', 'unknown')).issuperset(unit_states): |
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
937 |
return None |
1204.3.1
by Martin Packman
Fix bug introduced with unknown workloads and add test coverage |
938 |
unit_states.pop('unknown', None) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
939 |
return unit_states |
1196.1.6
by Martin Packman
Changes for review by abentley |
940 |
reporter = GroupReporter(sys.stdout, 'active') |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
941 |
self._wait_for_status(reporter, status_to_workloads, WorkloadsNotReady, |
1196.1.4
by Martin Packman
Finish increased sanity on deployer tests |
942 |
timeout=timeout, start=start) |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
943 |
|
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
944 |
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. |
945 |
""" Wait for a something (thing) matching none/all/some machines.
|
946 |
||
947 |
Examples:
|
|
948 |
wait_for('containers', 'all')
|
|
949 |
This will wait for a container to appear on all machines.
|
|
950 |
||
951 |
wait_for('machines-not-0', 'none')
|
|
952 |
This will wait for all machines other than 0 to be removed.
|
|
953 |
||
954 |
:param thing: string, either 'containers' or 'not-machine-0'
|
|
955 |
:param search_type: string containing none, some or all
|
|
956 |
:param timeout: number of seconds to wait for condition to be true.
|
|
957 |
:return:
|
|
958 |
"""
|
|
959 |
try: |
|
960 |
for status in self.status_until(timeout): |
|
961 |
hit = False |
|
962 |
miss = False |
|
963 |
||
964 |
for machine, details in status.status['machines'].iteritems(): |
|
965 |
if thing == 'containers': |
|
966 |
if 'containers' in details: |
|
967 |
hit = True |
|
968 |
else: |
|
969 |
miss = True |
|
970 |
||
971 |
elif thing == 'machines-not-0': |
|
972 |
if machine != '0': |
|
973 |
hit = True |
|
974 |
else: |
|
975 |
miss = True |
|
976 |
||
977 |
else: |
|
978 |
raise ValueError("Unrecognised thing to wait for: %s", |
|
979 |
thing) |
|
980 |
||
981 |
if search_type == 'none': |
|
982 |
if not hit: |
|
983 |
return
|
|
984 |
elif search_type == 'some': |
|
985 |
if hit: |
|
986 |
return
|
|
987 |
elif search_type == 'all': |
|
988 |
if not miss: |
|
989 |
return
|
|
990 |
except Exception: |
|
991 |
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. |
992 |
|
657.1.4
by Aaron Bentley
Move upgrade functionality to EnvJujuClient. |
993 |
def get_matching_agent_version(self, no_build=False): |
994 |
# strip the series and srch from the built version.
|
|
995 |
version_parts = self.version.split('-') |
|
996 |
if len(version_parts) == 4: |
|
997 |
version_number = '-'.join(version_parts[0:2]) |
|
998 |
else: |
|
999 |
version_number = version_parts[0] |
|
1000 |
if not no_build and self.env.local: |
|
1001 |
version_number += '.1' |
|
1002 |
return version_number |
|
1003 |
||
650.1.9
by Aaron Bentley
Merged trunk into compatibility-test. |
1004 |
def upgrade_juju(self, force_version=True): |
1005 |
args = () |
|
1006 |
if force_version: |
|
1007 |
version = self.get_matching_agent_version(no_build=True) |
|
1008 |
args += ('--version', version) |
|
657.1.4
by Aaron Bentley
Move upgrade functionality to EnvJujuClient. |
1009 |
if self.env.local: |
1010 |
args += ('--upload-tools',) |
|
659
by Aaron Bentley
Fix upgrade's juju invocation. |
1011 |
self.juju('upgrade-juju', args) |
657.1.4
by Aaron Bentley
Move upgrade functionality to EnvJujuClient. |
1012 |
|
1258.2.5
by Curtis Hovey
Added upgrade_mongo to EnvJujuClient. |
1013 |
def upgrade_mongo(self): |
1014 |
self.juju('upgrade-mongo', ()) |
|
1015 |
||
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
1016 |
def backup(self): |
1017 |
environ = self._shell_environ() |
|
808.2.6
by Aaron Bentley
Fix Azure terminate instances to work as Juju expects. |
1018 |
try: |
1080.1.14
by Aaron Bentley
Add docs. |
1019 |
# Mutate os.environ instead of supplying env parameter so Windows
|
1020 |
# can search env['PATH']
|
|
1080.1.10
by Aaron Bentley
Supply the shell environment via os.environ. |
1021 |
with scoped_environ(environ): |
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1022 |
args = self._full_args( |
1023 |
'create-backup', False, (), include_e=True) |
|
1326.1.2
by Aaron Bentley
Emit backup command args. |
1024 |
log.info(' '.join(args)) |
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1025 |
output = subprocess.check_output(args) |
808.2.6
by Aaron Bentley
Fix Azure terminate instances to work as Juju expects. |
1026 |
except subprocess.CalledProcessError as e: |
1091.4.1
by James Tunnicliffe
Merged upstream |
1027 |
log.info(e.output) |
808.2.6
by Aaron Bentley
Fix Azure terminate instances to work as Juju expects. |
1028 |
raise
|
1091.4.1
by James Tunnicliffe
Merged upstream |
1029 |
log.info(output) |
757.1.1
by Curtis Hovey
Support both .tgz and .tar.gz backup file names. |
1030 |
backup_file_pattern = re.compile('(juju-backup-[0-9-]+\.(t|tar.)gz)') |
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
1031 |
match = backup_file_pattern.search(output) |
1032 |
if match is None: |
|
1033 |
raise Exception("The backup file was not found in output: %s" % |
|
1034 |
output) |
|
1035 |
backup_file_name = match.group(1) |
|
1036 |
backup_file_path = os.path.abspath(backup_file_name) |
|
1091.4.1
by James Tunnicliffe
Merged upstream |
1037 |
log.info("State-Server backup at %s", backup_file_path) |
717.2.1
by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery. |
1038 |
return backup_file_path |
1039 |
||
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1040 |
def restore_backup(self, backup_file): |
1281.1.1
by Aaron Bentley
Rollback r1280 per Ian's email. |
1041 |
return self.get_juju_output('restore-backup', '-b', '--constraints', |
1042 |
'mem=2G', '--file', backup_file) |
|
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1043 |
|
1044 |
def restore_backup_async(self, backup_file): |
|
1281.1.1
by Aaron Bentley
Rollback r1280 per Ian's email. |
1045 |
return self.juju_async('restore-backup', ('-b', '--constraints', |
1046 |
'mem=2G', '--file', backup_file)) |
|
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1047 |
|
1048 |
def enable_ha(self): |
|
1049 |
self.juju('enable-ha', ('-n', '3')) |
|
1050 |
||
953.3.11
by Nate Finch
set longer timeout for fill log actions, and fix -e testing problem |
1051 |
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 |
1052 |
"""Fetches the results of the action with the given id.
|
1053 |
||
953.3.5
by Nate Finch
tests for new action stuff |
1054 |
Will wait for up to 1 minute for the action results.
|
953.3.7
by Nate Finch
update for code review comments |
1055 |
The action name here is just used for an more informational error in
|
953.3.5
by Nate Finch
tests for new action stuff |
1056 |
cases where it's available.
|
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
1057 |
Returns the yaml output of the fetched action.
|
1058 |
"""
|
|
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
1059 |
out = self.get_juju_output("show-action-output", id, "--wait", timeout) |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
1060 |
status = yaml_loads(out)["status"] |
1061 |
if status != "completed": |
|
953.3.8
by Nate Finch
more review changes |
1062 |
name = "" |
953.3.7
by Nate Finch
update for code review comments |
1063 |
if action is not None: |
953.3.8
by Nate Finch
more review changes |
1064 |
name = " " + action |
953.3.9
by Nate Finch
more code review changes |
1065 |
raise Exception( |
1066 |
"timed out waiting for action%s to complete during fetch" % |
|
1067 |
name) |
|
953.3.2
by Nate Finch
log_rot fixes |
1068 |
return out |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
1069 |
|
1070 |
def action_do(self, unit, action, *args): |
|
1071 |
"""Performs the given action on the given unit.
|
|
1072 |
||
1073 |
Action params should be given as args in the form foo=bar.
|
|
1074 |
Returns the id of the queued action.
|
|
1075 |
"""
|
|
953.3.13
by Nate Finch
more code review changes |
1076 |
args = (unit, action) + args |
1077 |
||
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
1078 |
output = self.get_juju_output("run-action", *args) |
953.3.9
by Nate Finch
more code review changes |
1079 |
action_id_pattern = re.compile( |
1080 |
'Action queued with id: ([a-f0-9\-]{36})') |
|
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
1081 |
match = action_id_pattern.search(output) |
1082 |
if match is None: |
|
1083 |
raise Exception("Action id not found in output: %s" % |
|
1084 |
output) |
|
1085 |
return match.group(1) |
|
1086 |
||
953.3.11
by Nate Finch
set longer timeout for fill log actions, and fix -e testing problem |
1087 |
def action_do_fetch(self, unit, action, timeout="1m", *args): |
983.1.1
by Martin Packman
Add new Remote class for accessing juju machines |
1088 |
"""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 |
1089 |
|
1090 |
Action params should be given as args in the form foo=bar.
|
|
1091 |
Returns the yaml output of the action.
|
|
1092 |
"""
|
|
1093 |
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 |
1094 |
return self.action_fetch(id, action, timeout) |
953.3.1
by Nate Finch
add log rotation test and support for actions in jujupy |
1095 |
|
1221.5.1
by Aaron Bentley
Extract EnvJujuClient.list_space. |
1096 |
def list_space(self): |
1221.5.2
by Aaron Bentley
Support list-space for EnvJujuClient. |
1097 |
return yaml.safe_load(self.get_juju_output('list-space')) |
1221.5.1
by Aaron Bentley
Extract EnvJujuClient.list_space. |
1098 |
|
1221.5.3
by Aaron Bentley
Extract EnvJujuClient.add_space. |
1099 |
def add_space(self, space): |
1221.5.4
by Aaron Bentley
Support space create => add-space. |
1100 |
self.juju('add-space', (space),) |
1221.5.3
by Aaron Bentley
Extract EnvJujuClient.add_space. |
1101 |
|
1221.5.5
by Aaron Bentley
Extract add_subnet. |
1102 |
def add_subnet(self, subnet, space): |
1221.5.6
by Aaron Bentley
Rename subnet add to add-subnet. |
1103 |
self.juju('add-subnet', (subnet, space)) |
1221.5.5
by Aaron Bentley
Extract add_subnet. |
1104 |
|
657.1.1
by Aaron Bentley
Extract EnvJujuClient from JujuClientDevel. |
1105 |
|
1315.2.2
by Aaron Bentley
Use --default-model in bootstrap. |
1106 |
class EnvJujuClient2B2(EnvJujuClient): |
1107 |
||
1108 |
def get_bootstrap_args(self, upload_tools, config_filename, |
|
1109 |
bootstrap_series=None): |
|
1110 |
"""Return the bootstrap arguments for the substrate."""
|
|
1111 |
if self.env.maas: |
|
1112 |
constraints = 'mem=2G arch=amd64' |
|
1113 |
elif self.env.joyent: |
|
1114 |
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
|
|
1115 |
constraints = 'mem=2G cpu-cores=1' |
|
1116 |
else: |
|
1117 |
constraints = 'mem=2G' |
|
1118 |
cloud_region = self.get_cloud_region(self.env.get_cloud(), |
|
1119 |
self.env.get_region()) |
|
1120 |
args = ['--constraints', constraints, self.env.environment, |
|
1121 |
cloud_region, '--config', config_filename] |
|
1122 |
if upload_tools: |
|
1123 |
args.insert(0, '--upload-tools') |
|
1124 |
else: |
|
1125 |
args.extend(['--agent-version', self.get_matching_agent_version()]) |
|
1126 |
||
1127 |
if bootstrap_series is not None: |
|
1128 |
args.extend(['--bootstrap-series', bootstrap_series]) |
|
1129 |
return tuple(args) |
|
1130 |
||
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
1131 |
def get_admin_client(self): |
1315.2.5
by Aaron Bentley
Update docs. |
1132 |
"""Return a client for the admin model. May return self."""
|
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
1133 |
return self |
1134 |
||
1135 |
def get_admin_model_name(self): |
|
1136 |
"""Return the name of the 'admin' model.
|
|
1137 |
||
1138 |
Return the name of the environment when an 'admin' model does
|
|
1139 |
not exist.
|
|
1140 |
"""
|
|
1141 |
models = self.get_models() |
|
1142 |
# The dict can be empty because 1.x does not support the models.
|
|
1143 |
# This is an ambiguous case for the jes feature flag which supports
|
|
1144 |
# multiple models, but none is named 'admin' by default. Since the
|
|
1145 |
# jes case also uses '-e' for models, the env is the admin model.
|
|
1146 |
for model in models.get('models', []): |
|
1147 |
if 'admin' in model['name']: |
|
1148 |
return 'admin' |
|
1149 |
return self.env.environment |
|
1150 |
||
1151 |
||
1152 |
class EnvJujuClient2A2(EnvJujuClient2B2): |
|
1258.1.3
by Aaron Bentley
Support transitional jujus. |
1153 |
"""Drives Juju 2.0-alpha2 clients."""
|
1154 |
||
1260.2.23
by Aaron Bentley
Test SimpleEnvironment/JujuData on init. |
1155 |
@classmethod
|
1156 |
def _get_env(cls, env): |
|
1157 |
if isinstance(env, JujuData): |
|
1158 |
raise ValueError( |
|
1159 |
'JujuData cannot be used with {}'.format(cls.__name__)) |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1160 |
return env |
1161 |
||
1258.1.3
by Aaron Bentley
Support transitional jujus. |
1162 |
def _shell_environ(self): |
1163 |
"""Generate a suitable shell environment.
|
|
1164 |
||
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1165 |
For 2.0-alpha2 set both JUJU_HOME and JUJU_DATA.
|
1258.1.3
by Aaron Bentley
Support transitional jujus. |
1166 |
"""
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1167 |
env = super(EnvJujuClient2A2, self)._shell_environ() |
1258.1.3
by Aaron Bentley
Support transitional jujus. |
1168 |
env['JUJU_HOME'] = self.env.juju_home |
1169 |
return env |
|
1170 |
||
1260.2.2
by Aaron Bentley
Implement support for the CPCs. |
1171 |
def bootstrap(self, upload_tools=False, bootstrap_series=None): |
1172 |
"""Bootstrap a controller."""
|
|
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
1173 |
self._check_bootstrap() |
1260.2.2
by Aaron Bentley
Implement support for the CPCs. |
1174 |
args = self.get_bootstrap_args(upload_tools, bootstrap_series) |
1175 |
self.juju('bootstrap', args, self.env.needs_sudo()) |
|
1176 |
||
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1177 |
@contextmanager
|
1178 |
def bootstrap_async(self, upload_tools=False): |
|
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
1179 |
self._check_bootstrap() |
1260.2.11
by Aaron Bentley
Get jujupy tests passing, remove region for manual. |
1180 |
args = self.get_bootstrap_args(upload_tools) |
1181 |
with self.juju_async('bootstrap', args): |
|
1182 |
yield
|
|
1183 |
log.info('Waiting for bootstrap of {}.'.format( |
|
1184 |
self.env.environment)) |
|
1185 |
||
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1186 |
def get_bootstrap_args(self, upload_tools, bootstrap_series=None): |
1187 |
"""Return the bootstrap arguments for the substrate."""
|
|
1260.2.30
by Aaron Bentley
Updates from review |
1188 |
constraints = self._get_substrate_constraints() |
1260.2.1
by Aaron Bentley
Spike to support cloud-credentials. |
1189 |
args = ('--constraints', constraints, |
1190 |
'--agent-version', self.get_matching_agent_version()) |
|
1191 |
if upload_tools: |
|
1192 |
args = ('--upload-tools',) + args |
|
1193 |
if bootstrap_series is not None: |
|
1194 |
args = args + ('--bootstrap-series', bootstrap_series) |
|
1195 |
return args |
|
1196 |
||
1258.1.3
by Aaron Bentley
Support transitional jujus. |
1197 |
|
1260.2.2
by Aaron Bentley
Implement support for the CPCs. |
1198 |
class EnvJujuClient2A1(EnvJujuClient2A2): |
1221.1.17
by Aaron Bentley
Provide for a separate 2.0-alpha1 client. |
1199 |
"""Drives Juju 2.0-alpha1 clients."""
|
1200 |
||
1221.5.15
by Aaron Bentley
Use 'show-status' instead of 'status'. |
1201 |
_show_status = 'status' |
1202 |
||
1240.2.1
by Aaron Bentley
Support models/cache.yaml |
1203 |
def get_cache_path(self): |
1204 |
return get_cache_path(self.env.juju_home, models=False) |
|
1205 |
||
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
1206 |
def _full_args(self, command, sudo, args, |
1207 |
timeout=None, include_e=True, admin=False): |
|
1221.1.19
by Aaron Bentley
Tweaked enough to bootstrap. |
1208 |
# sudo is not needed for devel releases.
|
1306.1.24
by Curtis Hovey
Added admin=False to _full_call_args and get_status(). |
1209 |
# admin is ignored. only environment exists.
|
1221.1.19
by Aaron Bentley
Tweaked enough to bootstrap. |
1210 |
if self.env is None or not include_e: |
1211 |
e_arg = () |
|
1212 |
else: |
|
1213 |
e_arg = ('-e', self.env.environment) |
|
1214 |
if timeout is None: |
|
1215 |
prefix = () |
|
1216 |
else: |
|
1217 |
prefix = get_timeout_prefix(timeout, self._timeout_path) |
|
1218 |
logging = '--debug' if self.debug else '--show-log' |
|
1219 |
||
1220 |
# If args is a string, make it a tuple. This makes writing commands
|
|
1221 |
# with one argument a bit nicer.
|
|
1222 |
if isinstance(args, basestring): |
|
1223 |
args = (args,) |
|
1224 |
# we split the command here so that the caller can control where the -e
|
|
1225 |
# <env> flag goes. Everything in the command string is put before the
|
|
1226 |
# -e flag.
|
|
1227 |
command = command.split() |
|
1228 |
return prefix + ('juju', logging,) + tuple(command) + e_arg + args |
|
1229 |
||
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
1230 |
def _shell_environ(self): |
1231 |
"""Generate a suitable shell environment.
|
|
1232 |
||
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1233 |
For 2.0-alpha1 and earlier set only JUJU_HOME and not JUJU_DATA.
|
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
1234 |
"""
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1235 |
env = super(EnvJujuClient2A1, self)._shell_environ() |
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
1236 |
env['JUJU_HOME'] = self.env.juju_home |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1237 |
del env['JUJU_DATA'] |
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
1238 |
return env |
1239 |
||
1221.4.2
by Aaron Bentley
Introduce EnvJujuClient.remove_service. |
1240 |
def remove_service(self, service): |
1241 |
self.juju('destroy-service', (service,)) |
|
1242 |
||
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1243 |
def backup(self): |
1244 |
environ = self._shell_environ() |
|
1245 |
# juju-backup does not support the -e flag.
|
|
1246 |
environ['JUJU_ENV'] = self.env.environment |
|
1247 |
try: |
|
1248 |
# Mutate os.environ instead of supplying env parameter so Windows
|
|
1249 |
# can search env['PATH']
|
|
1250 |
with scoped_environ(environ): |
|
1326.1.2
by Aaron Bentley
Emit backup command args. |
1251 |
args = ['juju', 'backup'] |
1252 |
log.info(' '.join(args)) |
|
1253 |
output = subprocess.check_output(args) |
|
1221.4.4
by Aaron Bentley
Support create-backup, restore-backup, enable-ha. |
1254 |
except subprocess.CalledProcessError as e: |
1255 |
log.info(e.output) |
|
1256 |
raise
|
|
1257 |
log.info(output) |
|
1258 |
backup_file_pattern = re.compile('(juju-backup-[0-9-]+\.(t|tar.)gz)') |
|
1259 |
match = backup_file_pattern.search(output) |
|
1260 |
if match is None: |
|
1261 |
raise Exception("The backup file was not found in output: %s" % |
|
1262 |
output) |
|
1263 |
backup_file_name = match.group(1) |
|
1264 |
backup_file_path = os.path.abspath(backup_file_name) |
|
1265 |
log.info("State-Server backup at %s", backup_file_path) |
|
1266 |
return backup_file_path |
|
1267 |
||
1268 |
def restore_backup(self, backup_file): |
|
1269 |
return self.get_juju_output('restore', '--constraints', 'mem=2G', |
|
1270 |
backup_file) |
|
1271 |
||
1272 |
def restore_backup_async(self, backup_file): |
|
1273 |
return self.juju_async('restore', ('--constraints', 'mem=2G', |
|
1274 |
backup_file)) |
|
1275 |
||
1276 |
def enable_ha(self): |
|
1277 |
self.juju('ensure-availability', ('-n', '3')) |
|
1278 |
||
1306.1.23
by Curtis Hovey
Added 1.x variations to list-controllers and list-models. |
1279 |
def list_models(self): |
1280 |
"""List the models registered with the current controller."""
|
|
1281 |
log.info('The model is environment {}'.format(self.env.environment)) |
|
1282 |
||
1283 |
def get_models(self): |
|
1284 |
"""return a models dict with a 'models': [] key-value pair."""
|
|
1285 |
return {} |
|
1286 |
||
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
1287 |
def _get_models(self): |
1288 |
"""return a list of model dicts."""
|
|
1289 |
# In 2.0-alpha1, 'list-models' produced a yaml list rather than a
|
|
1290 |
# dict, but the command and parsing are the same.
|
|
1291 |
return super(EnvJujuClient2A1, self).get_models() |
|
1292 |
||
1306.1.23
by Curtis Hovey
Added 1.x variations to list-controllers and list-models. |
1293 |
def list_controllers(self): |
1294 |
"""List the controllers."""
|
|
1295 |
log.info( |
|
1296 |
'The controller is environment {}'.format(self.env.environment)) |
|
1297 |
||
1259.1.2
by Aaron Bentley
Implement get_controller_member_status. |
1298 |
@staticmethod
|
1299 |
def get_controller_member_status(info_dict): |
|
1300 |
return info_dict.get('state-server-member-status') |
|
1259.1.1
by Aaron Bentley
Expect controller-member-status in 2.0-alpha2 |
1301 |
|
1221.1.25
by Aaron Bentley
Use flat names for action commands. |
1302 |
def action_fetch(self, id, action=None, timeout="1m"): |
1303 |
"""Fetches the results of the action with the given id.
|
|
1304 |
||
1305 |
Will wait for up to 1 minute for the action results.
|
|
1306 |
The action name here is just used for an more informational error in
|
|
1307 |
cases where it's available.
|
|
1308 |
Returns the yaml output of the fetched action.
|
|
1309 |
"""
|
|
1310 |
# the command has to be "action fetch" so that the -e <env> args are
|
|
1311 |
# placed after "fetch", since that's where action requires them to be.
|
|
1312 |
out = self.get_juju_output("action fetch", id, "--wait", timeout) |
|
1313 |
status = yaml_loads(out)["status"] |
|
1314 |
if status != "completed": |
|
1315 |
name = "" |
|
1316 |
if action is not None: |
|
1317 |
name = " " + action |
|
1318 |
raise Exception( |
|
1319 |
"timed out waiting for action%s to complete during fetch" % |
|
1320 |
name) |
|
1321 |
return out |
|
1322 |
||
1323 |
def action_do(self, unit, action, *args): |
|
1324 |
"""Performs the given action on the given unit.
|
|
1325 |
||
1326 |
Action params should be given as args in the form foo=bar.
|
|
1327 |
Returns the id of the queued action.
|
|
1328 |
"""
|
|
1329 |
args = (unit, action) + args |
|
1330 |
||
1331 |
# the command has to be "action do" so that the -e <env> args are
|
|
1332 |
# placed after "do", since that's where action requires them to be.
|
|
1333 |
output = self.get_juju_output("action do", *args) |
|
1334 |
action_id_pattern = re.compile( |
|
1335 |
'Action queued with id: ([a-f0-9\-]{36})') |
|
1336 |
match = action_id_pattern.search(output) |
|
1337 |
if match is None: |
|
1338 |
raise Exception("Action id not found in output: %s" % |
|
1339 |
output) |
|
1340 |
return match.group(1) |
|
1341 |
||
1221.5.2
by Aaron Bentley
Support list-space for EnvJujuClient. |
1342 |
def list_space(self): |
1343 |
return yaml.safe_load(self.get_juju_output('space list')) |
|
1344 |
||
1221.5.4
by Aaron Bentley
Support space create => add-space. |
1345 |
def add_space(self, space): |
1346 |
self.juju('space create', (space),) |
|
1347 |
||
1221.5.6
by Aaron Bentley
Rename subnet add to add-subnet. |
1348 |
def add_subnet(self, subnet, space): |
1349 |
self.juju('subnet add', (subnet, space)) |
|
1350 |
||
1251.1.1
by Aaron Bentley
Implement and use set_model_constraints. |
1351 |
def set_model_constraints(self, constraints): |
1352 |
constraint_strings = self._dict_as_option_strings(constraints) |
|
1353 |
return self.juju('set-constraints', constraint_strings) |
|
1354 |
||
1239.1.1
by Aaron Bentley
Implement and use EnvJujuClient.set_config. |
1355 |
def set_config(self, service, options): |
1356 |
option_strings = ['{}={}'.format(*item) for item in options.items()] |
|
1357 |
self.juju('set', (service,) + tuple(option_strings)) |
|
1358 |
||
1221.5.12
by Aaron Bentley
Support get-config. |
1359 |
def get_config(self, service): |
1360 |
return yaml_loads(self.get_juju_output('get', service)) |
|
1361 |
||
1243.1.1
by Aaron Bentley
Support rename of get-env/set-env => get-model-config/set-model-config. |
1362 |
def get_model_config(self): |
1363 |
"""Return the value of the environment's configured option."""
|
|
1364 |
return yaml.safe_load(self.get_juju_output('get-env')) |
|
1365 |
||
1366 |
def get_env_option(self, option): |
|
1367 |
"""Return the value of the environment's configured option."""
|
|
1368 |
return self.get_juju_output('get-env', option) |
|
1369 |
||
1370 |
def set_env_option(self, option, value): |
|
1371 |
"""Set the value of the option in the environment."""
|
|
1372 |
option_value = "%s=%s" % (option, value) |
|
1373 |
return self.juju('set-env', (option_value,)) |
|
1374 |
||
1221.1.17
by Aaron Bentley
Provide for a separate 2.0-alpha1 client. |
1375 |
|
1221.1.21
by Aaron Bentley
Support -m for 2.0-alpha2. |
1376 |
class EnvJujuClient1X(EnvJujuClient2A1): |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1377 |
"""Base for all 1.x client drivers."""
|
1378 |
||
1251.2.4
by Aaron Bentley
Rename bootstrap_supports => bootstrap_replaces |
1379 |
# The environments.yaml options that are replaced by bootstrap options.
|
1380 |
# For Juju 1.x, no bootstrap options are used.
|
|
1381 |
bootstrap_replaces = frozenset() |
|
1242.3.7
by Aaron Bentley
BootstrapManager support for bootstrap option. |
1382 |
|
1251.2.2
by Aaron Bentley
Remove support for --to. |
1383 |
def get_bootstrap_args(self, upload_tools, bootstrap_series=None): |
1251.2.7
by Aaron Bentley
Update docs |
1384 |
"""Return the bootstrap arguments for the substrate."""
|
1260.2.30
by Aaron Bentley
Updates from review |
1385 |
constraints = self._get_substrate_constraints() |
1242.3.1
by Aaron Bentley
Update get_bootstrap_args. |
1386 |
args = ('--constraints', constraints) |
1387 |
if upload_tools: |
|
1388 |
args = ('--upload-tools',) + args |
|
1389 |
if bootstrap_series is not None: |
|
1390 |
env_val = self.env.config.get('default-series') |
|
1391 |
if bootstrap_series != env_val: |
|
1392 |
raise BootstrapMismatch( |
|
1393 |
'bootstrap-series', bootstrap_series, 'default-series', |
|
1394 |
env_val) |
|
1395 |
return args |
|
1396 |
||
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
1397 |
def get_jes_command(self): |
1398 |
"""Return the JES command to destroy a controller.
|
|
1399 |
||
1400 |
Juju 2.x has 'kill-controller'.
|
|
1401 |
Some intermediate versions had 'controller kill'.
|
|
1402 |
Juju 1.25 has 'system kill' when the jes feature flag is set.
|
|
1403 |
||
1404 |
:raises: JESNotSupported when the version of Juju does not expose
|
|
1405 |
a JES command.
|
|
1406 |
:return: The JES command.
|
|
1407 |
"""
|
|
1408 |
commands = self.get_juju_output('help', 'commands', include_e=False) |
|
1409 |
for line in commands.splitlines(): |
|
1410 |
for cmd in _jes_cmds.keys(): |
|
1411 |
if line.startswith(cmd): |
|
1412 |
return cmd |
|
1413 |
raise JESNotSupported() |
|
1414 |
||
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1415 |
def create_environment(self, controller_client, config_file): |
1416 |
seen_cmd = self.get_jes_command() |
|
1242.2.1
by Aaron Bentley
EnvJujuClient assumes JES enabled, since it assumes 2.0. |
1417 |
if seen_cmd == SYSTEM: |
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1418 |
controller_option = ('-s', controller_client.env.environment) |
1419 |
else: |
|
1420 |
controller_option = ('-c', controller_client.env.environment) |
|
1421 |
self.juju(_jes_cmds[seen_cmd]['create'], controller_option + ( |
|
1422 |
self.env.environment, '--config', config_file), include_e=False) |
|
1423 |
||
1221.1.15
by Aaron Bentley
Implement and use destroy_model for destroying models. |
1424 |
def destroy_model(self): |
1425 |
"""With JES enabled, destroy-environment destroys the model."""
|
|
1426 |
self.destroy_environment(force=False) |
|
1427 |
||
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1428 |
def destroy_environment(self, force=True, delete_jenv=False): |
1429 |
if force: |
|
1430 |
force_arg = ('--force',) |
|
1431 |
else: |
|
1432 |
force_arg = () |
|
1433 |
exit_status = self.juju( |
|
1434 |
'destroy-environment', |
|
1435 |
(self.env.environment,) + force_arg + ('-y',), |
|
1436 |
self.env.needs_sudo(), check=False, include_e=False, |
|
1437 |
timeout=timedelta(minutes=10).total_seconds()) |
|
1438 |
if delete_jenv: |
|
1439 |
jenv_path = get_jenv_path(self.env.juju_home, self.env.environment) |
|
1440 |
ensure_deleted(jenv_path) |
|
1441 |
return exit_status |
|
1442 |
||
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
1443 |
def _get_models(self): |
1444 |
"""return a list of model dicts."""
|
|
1445 |
return yaml.safe_load(self.get_juju_output( |
|
1446 |
'environments', '-s', self.env.environment, '--format', 'yaml', |
|
1447 |
include_e=False)) |
|
1448 |
||
1259.2.1
by Martin Packman
Add flag to run_deployer to use juju 2.0 bundle deployment |
1449 |
def deploy_bundle(self, bundle, timeout=_DEFAULT_BUNDLE_TIMEOUT): |
1450 |
"""Deploy bundle using deployer for Juju 1.X version."""
|
|
1451 |
self.deployer(bundle, timeout=timeout) |
|
1452 |
||
1306.1.5
by Curtis Hovey
Added get_controller_endpoint for get_endpoints. |
1453 |
def get_controller_endpoint(self): |
1454 |
"""Return the address of the state-server leader."""
|
|
1306.1.19
by Curtis Hovey
Fix calls to show-controller with include_e=False. |
1455 |
endpoint = self.get_juju_output('api-endpoints') |
1310.1.2
by Curtis Hovey
Address ipv6 split address and port. |
1456 |
address, port = split_address_port(endpoint) |
1306.1.5
by Curtis Hovey
Added get_controller_endpoint for get_endpoints. |
1457 |
return address |
1458 |
||
1258.2.5
by Curtis Hovey
Added upgrade_mongo to EnvJujuClient. |
1459 |
def upgrade_mongo(self): |
1460 |
raise UpgradeMongoNotSupported() |
|
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1461 |
|
1462 |
||
1463 |
class EnvJujuClient22(EnvJujuClient1X): |
|
953.3.9
by Nate Finch
more code review changes |
1464 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1465 |
used_feature_flags = frozenset(['actions']) |
953.3.9
by Nate Finch
more code review changes |
1466 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1467 |
def __init__(self, *args, **kwargs): |
1468 |
super(EnvJujuClient22, self).__init__(*args, **kwargs) |
|
1469 |
self.feature_flags.add('actions') |
|
953.3.9
by Nate Finch
more code review changes |
1470 |
|
1471 |
||
1221.1.1
by Aaron Bentley
Support -model renames in jujupy and test_jujupy. |
1472 |
class EnvJujuClient26(EnvJujuClient1X): |
1074.2.1
by Aaron Bentley
Add EnvJujuClient support for Juju 2.6. |
1473 |
"""Drives Juju 2.6-series clients."""
|
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
1474 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1475 |
used_feature_flags = frozenset(['address-allocation', 'cloudsigma', 'jes']) |
1476 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
1477 |
def __init__(self, *args, **kwargs): |
1074.2.1
by Aaron Bentley
Add EnvJujuClient support for Juju 2.6. |
1478 |
super(EnvJujuClient26, self).__init__(*args, **kwargs) |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1479 |
if self.env is None or self.env.config is None: |
1480 |
return
|
|
1481 |
if self.env.config.get('type') == 'cloudsigma': |
|
1482 |
self.feature_flags.add('cloudsigma') |
|
1221.1.8
by Aaron Bentley
Implement EnvJujuClient.clone. |
1483 |
|
1044.1.9
by Aaron Bentley
Update and add tests. |
1484 |
def enable_jes(self): |
1145.2.3
by Curtis Hovey
Fix get_jes_command to exit the loop and set _jes_command correctly. |
1485 |
"""Enable JES if JES is optional.
|
1486 |
||
1487 |
:raises: JESByDefault when JES is always enabled; Juju has the
|
|
1160.1.4
by Curtis Hovey
Fix comments. |
1488 |
'destroy-controller' command.
|
1145.2.3
by Curtis Hovey
Fix get_jes_command to exit the loop and set _jes_command correctly. |
1489 |
:raises: JESNotSupported when JES is not supported; Juju does not have
|
1160.1.4
by Curtis Hovey
Fix comments. |
1490 |
the 'system kill' command when the JES feature flag is set.
|
1145.2.3
by Curtis Hovey
Fix get_jes_command to exit the loop and set _jes_command correctly. |
1491 |
"""
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1492 |
|
1493 |
if 'jes' in self.feature_flags: |
|
1044.1.9
by Aaron Bentley
Update and add tests. |
1494 |
return
|
1145.2.15
by Curtis Hovey
Do not check for controller in enable_jes. |
1495 |
if self.is_jes_enabled(): |
1044.1.9
by Aaron Bentley
Update and add tests. |
1496 |
raise JESByDefault() |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1497 |
self.feature_flags.add('jes') |
1050.2.1
by Aaron Bentley
Make is_jes_enabled public. |
1498 |
if not self.is_jes_enabled(): |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1499 |
self.feature_flags.remove('jes') |
1044.1.9
by Aaron Bentley
Update and add tests. |
1500 |
raise JESNotSupported() |
1501 |
||
1162.2.20
by Aaron Bentley
Implement disable_jes. |
1502 |
def disable_jes(self): |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1503 |
if 'jes' in self.feature_flags: |
1504 |
self.feature_flags.remove('jes') |
|
1162.2.20
by Aaron Bentley
Implement disable_jes. |
1505 |
|
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
1506 |
def enable_container_address_allocation(self): |
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1507 |
self.feature_flags.add('address-allocation') |
928.2.2
by Aaron Bentley
Auto-enable support for cloudsigma in 1.24. |
1508 |
|
1509 |
||
1074.2.1
by Aaron Bentley
Add EnvJujuClient support for Juju 2.6. |
1510 |
class EnvJujuClient25(EnvJujuClient26): |
1511 |
"""Drives Juju 2.5-series clients."""
|
|
1512 |
||
1513 |
||
957.2.1
by Aaron Bentley
Treat cloudsigma as provisional in 2.5 |
1514 |
class EnvJujuClient24(EnvJujuClient25): |
1044.1.17
by Aaron Bentley
Fix docs |
1515 |
"""Similar to EnvJujuClient25, but lacking JES support."""
|
957.2.1
by Aaron Bentley
Treat cloudsigma as provisional in 2.5 |
1516 |
|
1263.1.1
by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method |
1517 |
used_feature_flags = frozenset(['cloudsigma']) |
1518 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
1519 |
def enable_jes(self): |
1520 |
raise JESNotSupported() |
|
1521 |
||
1256.1.2
by Aaron Bentley
Expect old add-machine behaviour from 1.24.x and earlier. |
1522 |
def add_ssh_machines(self, machines): |
1523 |
for machine in machines: |
|
1524 |
self.juju('add-machine', ('ssh:' + machine,)) |
|
1525 |
||
957.2.1
by Aaron Bentley
Treat cloudsigma as provisional in 2.5 |
1526 |
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1527 |
def get_local_root(juju_home, env): |
1528 |
return os.path.join(juju_home, env.environment) |
|
1529 |
||
1530 |
||
663.1.8
by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env. |
1531 |
def bootstrap_from_env(juju_home, client): |
1532 |
with temp_bootstrap_env(juju_home, client): |
|
1533 |
client.bootstrap() |
|
1534 |
||
1535 |
||
796.2.4
by John George
Added support for setting the juju path, series and agent_url. |
1536 |
def quickstart_from_env(juju_home, client, bundle): |
1537 |
with temp_bootstrap_env(juju_home, client): |
|
1538 |
client.quickstart(bundle) |
|
1539 |
||
1540 |
||
1162.2.22
by Aaron Bentley
Extract maybe_jes to clarify logic. |
1541 |
@contextmanager
|
1542 |
def maybe_jes(client, jes_enabled, try_jes): |
|
1162.2.23
by Aaron Bentley
Update docs. |
1543 |
"""If JES is desired and not enabled, try to enable it for this context.
|
1162.2.22
by Aaron Bentley
Extract maybe_jes to clarify logic. |
1544 |
|
1545 |
JES will be in its previous state after exiting this context.
|
|
1546 |
If jes_enabled is True or try_jes is False, the context is a no-op.
|
|
1162.2.23
by Aaron Bentley
Update docs. |
1547 |
If enable_jes() raises JESNotSupported, JES will not be enabled in the
|
1548 |
context.
|
|
1162.2.22
by Aaron Bentley
Extract maybe_jes to clarify logic. |
1549 |
|
1550 |
The with value is True if JES is enabled in the context.
|
|
1551 |
"""
|
|
1552 |
||
1553 |
class JESUnwanted(Exception): |
|
1554 |
"""Non-error. Used to avoid enabling JES if not wanted."""
|
|
1555 |
||
1556 |
try: |
|
1557 |
if not try_jes or jes_enabled: |
|
1558 |
raise JESUnwanted |
|
1559 |
client.enable_jes() |
|
1560 |
except (JESNotSupported, JESUnwanted): |
|
1561 |
yield jes_enabled |
|
1562 |
return
|
|
1563 |
else: |
|
1564 |
try: |
|
1565 |
yield True |
|
1566 |
finally: |
|
1567 |
client.disable_jes() |
|
1568 |
||
1569 |
||
1162.2.21
by Aaron Bentley
Implement and use tear_down(try_jes=True) |
1570 |
def tear_down(client, jes_enabled, try_jes=False): |
1571 |
"""Tear down a JES or non-JES environment.
|
|
1572 |
||
1199
by Curtis Hovey
Revert lp:~sinzui/juju-ci-tools/kill-controller-command r1198 because it exposes that controllers are |
1573 |
JES environments are torn down via 'controller kill' or 'system kill',
|
1162.2.21
by Aaron Bentley
Implement and use tear_down(try_jes=True) |
1574 |
and non-JES environments are torn down via 'destroy-environment --force.'
|
1575 |
"""
|
|
1162.2.22
by Aaron Bentley
Extract maybe_jes to clarify logic. |
1576 |
with maybe_jes(client, jes_enabled, try_jes) as jes_enabled: |
1577 |
if jes_enabled: |
|
1578 |
client.kill_controller() |
|
1162.2.21
by Aaron Bentley
Implement and use tear_down(try_jes=True) |
1579 |
else: |
1210.1.1
by Aaron Bentley
Destroy environment without --force if possible. |
1580 |
if client.destroy_environment(force=False) != 0: |
1581 |
client.destroy_environment(force=True) |
|
1162.2.21
by Aaron Bentley
Implement and use tear_down(try_jes=True) |
1582 |
|
1583 |
||
696.1.10
by Aaron Bentley
Move uniquify_local to jujupy. |
1584 |
def uniquify_local(env): |
1585 |
"""Ensure that local environments have unique port settings.
|
|
1586 |
||
1587 |
This allows local environments to be duplicated despite
|
|
1588 |
https://bugs.launchpad.net/bugs/1382131
|
|
1589 |
"""
|
|
1590 |
if not env.local: |
|
1591 |
return
|
|
1592 |
port_defaults = { |
|
1593 |
'api-port': 17070, |
|
1594 |
'state-port': 37017, |
|
1595 |
'storage-port': 8040, |
|
1596 |
'syslog-port': 6514, |
|
1597 |
}
|
|
1598 |
for key, default in port_defaults.items(): |
|
1599 |
env.config[key] = env.config.get(key, default) + 1 |
|
1600 |
||
1601 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
1602 |
def dump_environments_yaml(juju_home, config): |
1603 |
environments_path = get_environments_path(juju_home) |
|
1604 |
with open(environments_path, 'w') as config_file: |
|
1605 |
yaml.safe_dump(config, config_file) |
|
1606 |
||
1607 |
||
663.1.8
by Aaron Bentley
Extract temp_bootstrap_env from bootstrap_from_env. |
1608 |
@contextmanager
|
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
1609 |
def _temp_env(new_config, parent=None, set_home=True): |
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
1610 |
"""Use the supplied config as juju environment.
|
1611 |
||
1612 |
This is not a fully-formed version for bootstrapping. See
|
|
1613 |
temp_bootstrap_env.
|
|
1614 |
"""
|
|
1615 |
with temp_dir(parent) as temp_juju_home: |
|
1044.1.9
by Aaron Bentley
Update and add tests. |
1616 |
dump_environments_yaml(temp_juju_home, new_config) |
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
1617 |
if set_home: |
1618 |
context = scoped_environ() |
|
1619 |
else: |
|
1620 |
context = nested() |
|
1621 |
with context: |
|
1622 |
if set_home: |
|
1623 |
os.environ['JUJU_HOME'] = temp_juju_home |
|
1258.1.1
by Aaron Bentley
Use JUJU_DATA where client may be 2.0alpha2+ |
1624 |
os.environ['JUJU_DATA'] = temp_juju_home |
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
1625 |
yield temp_juju_home |
1626 |
||
1627 |
||
1044.1.9
by Aaron Bentley
Update and add tests. |
1628 |
def jes_home_path(juju_home, dir_name): |
1048.1.3
by Aaron Bentley
Add make_jes_home and jes_home_path to tests. |
1629 |
return os.path.join(juju_home, 'jes-homes', dir_name) |
1044.1.9
by Aaron Bentley
Update and add tests. |
1630 |
|
1631 |
||
1240.2.1
by Aaron Bentley
Support models/cache.yaml |
1632 |
def get_cache_path(juju_home, models=False): |
1633 |
if models: |
|
1634 |
root = os.path.join(juju_home, 'models') |
|
1635 |
else: |
|
1636 |
root = os.path.join(juju_home, 'environments') |
|
1637 |
return os.path.join(root, 'cache.yaml') |
|
1050.2.2
by Aaron Bentley
Retain config.yaml instead of .jenv for JES. |
1638 |
|
1639 |
||
1242.3.16
by Aaron Bentley
Make agent-version omission client-dependent. |
1640 |
def make_safe_config(client): |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1641 |
config = dict(client.env.config) |
1242.3.18
by Aaron Bentley
Rename bootstrap_supports to bootstrap_replaces. |
1642 |
if 'agent-version' in client.bootstrap_replaces: |
1242.3.16
by Aaron Bentley
Make agent-version omission client-dependent. |
1643 |
config.pop('agent-version', None) |
1644 |
else: |
|
1242.3.10
by Aaron Bentley
Base support for agent-version. |
1645 |
config['agent-version'] = client.get_matching_agent_version() |
709.1.1
by Aaron Bentley
Always set test-mode to True. |
1646 |
# AFAICT, we *always* want to set test-mode to True. If we ever find a
|
1647 |
# use-case where we don't, we can make this optional.
|
|
1648 |
config['test-mode'] = True |
|
990.3.1
by Curtis Hovey
Ensure the env name is in the config. |
1649 |
# Explicitly set 'name', which Juju implicitly sets to env.environment to
|
1650 |
# ensure MAASAccount knows what the name will be.
|
|
1651 |
config['name'] = client.env.environment |
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1652 |
if config['type'] == 'local': |
1193.2.5
by Aaron Bentley
Remove EnvJujuClient.juju_home completely. |
1653 |
config.setdefault('root-dir', get_local_root(client.env.juju_home, |
995.1.14
by Aaron Bentley
Destroy environments. |
1654 |
client.env)) |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1655 |
# MongoDB requires a lot of free disk space, and the only
|
1656 |
# visible error message is from "juju bootstrap":
|
|
1657 |
# "cannot initiate replication set" if disk space is low.
|
|
1658 |
# What "low" exactly means, is unclear, but 8GB should be
|
|
1659 |
# enough.
|
|
1660 |
ensure_dir(config['root-dir']) |
|
1661 |
check_free_disk_space(config['root-dir'], 8000000, "MongoDB files") |
|
1662 |
if client.env.kvm: |
|
1663 |
check_free_disk_space( |
|
1664 |
"/var/lib/uvtool/libvirt/images", 2000000, |
|
1665 |
"KVM disk files") |
|
1666 |
else: |
|
1667 |
check_free_disk_space( |
|
1668 |
"/var/lib/lxc", 2000000, "LXC containers") |
|
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
1669 |
return config |
1670 |
||
1671 |
||
1672 |
@contextmanager
|
|
1242.3.16
by Aaron Bentley
Make agent-version omission client-dependent. |
1673 |
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. |
1674 |
"""Create a temporary environment for bootstrapping.
|
1675 |
||
1676 |
This involves creating a temporary juju home directory and returning its
|
|
1677 |
location.
|
|
1678 |
||
1679 |
:param set_home: Set JUJU_HOME to match the temporary home in this
|
|
1680 |
context. If False, juju_home should be supplied to bootstrap.
|
|
1681 |
"""
|
|
1682 |
new_config = { |
|
1251.2.6
by Aaron Bentley
Remove spurious change. |
1683 |
'environments': {client.env.environment: make_safe_config(client)}} |
995.1.13
by Aaron Bentley
Get assess_jes_deploy running to completion. |
1684 |
# Always bootstrap a matching environment.
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1685 |
jenv_path = get_jenv_path(juju_home, client.env.environment) |
1044.1.9
by Aaron Bentley
Update and add tests. |
1686 |
if permanent: |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1687 |
context = client.env.make_jes_home( |
1688 |
juju_home, client.env.environment, new_config) |
|
1044.1.9
by Aaron Bentley
Update and add tests. |
1689 |
else: |
1690 |
context = _temp_env(new_config, juju_home, set_home) |
|
1691 |
with context as temp_juju_home: |
|
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1692 |
if os.path.lexists(jenv_path): |
1693 |
raise Exception('%s already exists!' % jenv_path) |
|
1694 |
new_jenv_path = get_jenv_path(temp_juju_home, client.env.environment) |
|
1695 |
# Create a symlink to allow access while bootstrapping, and to reduce
|
|
1696 |
# races. Can't use a hard link because jenv doesn't exist until
|
|
1697 |
# partway through bootstrap.
|
|
1698 |
ensure_dir(os.path.join(juju_home, 'environments')) |
|
1044.1.1
by Aaron Bentley
Use the deploy_job script everywhere, fix windows weirdness. |
1699 |
# Skip creating symlink where not supported (i.e. Windows).
|
1048.1.4
by Aaron Bentley
Improve condition order. |
1700 |
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. |
1701 |
os.symlink(new_jenv_path, jenv_path) |
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
1702 |
old_juju_home = client.env.juju_home |
1703 |
client.env.juju_home = temp_juju_home |
|
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
1704 |
try: |
807.1.1
by Aaron Bentley
Avoid creating temp juju homes inside temp juju homes. |
1705 |
yield temp_juju_home |
716.1.2
by Aaron Bentley
Extract _temp_env from temp_bootstrap_env. |
1706 |
finally: |
1044.1.9
by Aaron Bentley
Update and add tests. |
1707 |
if not permanent: |
1708 |
# replace symlink with file before deleting temp home.
|
|
1709 |
try: |
|
1710 |
os.rename(new_jenv_path, jenv_path) |
|
1711 |
except OSError as e: |
|
1712 |
if e.errno != errno.ENOENT: |
|
1713 |
raise
|
|
1714 |
# Remove dangling symlink
|
|
1048.1.5
by Aaron Bentley
Updates from review. |
1715 |
try: |
1716 |
os.unlink(jenv_path) |
|
1717 |
except OSError as e: |
|
1718 |
if e.errno != errno.ENOENT: |
|
1719 |
raise
|
|
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
1720 |
client.env.juju_home = old_juju_home |
663.1.2
by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests. |
1721 |
|
1722 |
||
1153.4.3
by Martin Packman
Changes from review by abentley |
1723 |
def get_machine_dns_name(client, machine, timeout=600): |
1724 |
"""Wait for dns-name on a juju machine."""
|
|
1725 |
for status in client.status_until(timeout=timeout): |
|
1726 |
try: |
|
1727 |
return _dns_name_for_machine(status, machine) |
|
1728 |
except KeyError: |
|
1153.4.4
by Martin Packman
Log in jujupy context |
1729 |
log.debug("No dns-name yet for machine %s", machine) |
1153.4.3
by Martin Packman
Changes from review by abentley |
1730 |
|
1731 |
||
1732 |
def _dns_name_for_machine(status, machine): |
|
1733 |
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 |
1734 |
if is_ipv6_address(host): |
1735 |
log.warning("Selected IPv6 address for machine %s: %r", machine, host) |
|
1736 |
return host |
|
1153.4.3
by Martin Packman
Changes from review by abentley |
1737 |
|
1738 |
||
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
1739 |
class Controller: |
1740 |
||
1741 |
def __init__(self, name): |
|
1742 |
self.name = name |
|
1743 |
||
1744 |
||
34.1.3
by Aaron Bentley
Add status and environment tests. |
1745 |
class Status: |
1746 |
||
754.1.1
by Aaron Bentley
Emit status text for agent timeout. |
1747 |
def __init__(self, status, status_text): |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1748 |
self.status = status |
754.1.1
by Aaron Bentley
Emit status text for agent timeout. |
1749 |
self.status_text = status_text |
1750 |
||
1751 |
@classmethod
|
|
1752 |
def from_text(cls, text): |
|
1753 |
status_yaml = yaml_loads(text) |
|
1754 |
return cls(status_yaml, text) |
|
34.1.3
by Aaron Bentley
Add status and environment tests. |
1755 |
|
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
1756 |
def iter_machines(self, containers=False, machines=True): |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1757 |
for machine_name, machine in sorted(self.status['machines'].items()): |
1091.3.1
by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment. |
1758 |
if machines: |
1759 |
yield machine_name, machine |
|
805.1.3
by Aaron Bentley
Allow iterating through container machines. |
1760 |
if containers: |
1761 |
for contained, unit in machine.get('containers', {}).items(): |
|
1762 |
yield contained, unit |
|
419.1.12
by Aaron Bentley
Add wait for ha to come up. |
1763 |
|
703.1.3
by Aaron Bentley
Start implementing deploy-many stage. |
1764 |
def iter_new_machines(self, old_status): |
1765 |
for machine, data in self.iter_machines(): |
|
1766 |
if machine in old_status.status['machines']: |
|
1767 |
continue
|
|
1768 |
yield machine, data |
|
1769 |
||
1196.1.6
by Martin Packman
Changes for review by abentley |
1770 |
def iter_units(self): |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1771 |
for service_name, service in sorted(self.status['services'].items()): |
1772 |
for unit_name, unit in sorted(service.get('units', {}).items()): |
|
1773 |
yield unit_name, unit |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
1774 |
subordinates = unit.get('subordinates', ()) |
1775 |
for sub_name in sorted(subordinates): |
|
1776 |
yield sub_name, subordinates[sub_name] |
|
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1777 |
|
419.1.12
by Aaron Bentley
Add wait for ha to come up. |
1778 |
def agent_items(self): |
805.1.3
by Aaron Bentley
Allow iterating through container machines. |
1779 |
for machine_name, machine in self.iter_machines(containers=True): |
650.1.8
by Aaron Bentley
Implement most compatibility testing. |
1780 |
yield machine_name, machine |
1196.1.1
by Martin Packman
Refactoring jujupy including new wait_for_workloads and iter_units methods |
1781 |
for unit_name, unit in self.iter_units(): |
1782 |
yield unit_name, unit |
|
34.1.3
by Aaron Bentley
Add status and environment tests. |
1783 |
|
1784 |
def agent_states(self): |
|
1785 |
"""Map agent states to the units and machines in those states."""
|
|
1786 |
states = defaultdict(list) |
|
1787 |
for item_name, item in self.agent_items(): |
|
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
1788 |
states[coalesce_agent_status(item)].append(item_name) |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1789 |
return states |
1790 |
||
657.1.5
by Aaron Bentley
Move wait_for_started to EnvJujuClient. |
1791 |
def check_agents_started(self, environment_name=None): |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1792 |
"""Check whether all agents are in the 'started' state.
|
1793 |
||
1794 |
If not, return agent_states output. If so, return None.
|
|
1795 |
If an error is encountered for an agent, raise ErroredUnit
|
|
1796 |
"""
|
|
771.2.1
by Aaron Bentley
Increase set of agent-state-infos that indicate failure. |
1797 |
bad_state_info = re.compile( |
1798 |
'(.*error|^(cannot set up groups|cannot run instance)).*') |
|
34.1.3
by Aaron Bentley
Add status and environment tests. |
1799 |
for item_name, item in self.agent_items(): |
1800 |
state_info = item.get('agent-state-info', '') |
|
771.2.1
by Aaron Bentley
Increase set of agent-state-infos that indicate failure. |
1801 |
if bad_state_info.match(state_info): |
301.1.3
by Aaron Bentley
Remove environment name from log messages and errors. |
1802 |
raise ErroredUnit(item_name, state_info) |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1803 |
states = self.agent_states() |
1246.1.2
by Curtis Hovey
Added coalesce_agent_status to handle machine agent-state and unit agent-status. |
1804 |
if set(states.keys()).issubset(AGENTS_READY): |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1805 |
return None |
1806 |
for state, entries in states.items(): |
|
1807 |
if 'error' in state: |
|
966.2.1
by Curtis Hovey
Added extra_env, but tests fail. |
1808 |
raise ErroredUnit(entries[0], state) |
34.1.3
by Aaron Bentley
Add status and environment tests. |
1809 |
return states |
1810 |
||
796.2.1
by John George
Add quickstart_deploy.py |
1811 |
def get_service_count(self): |
1812 |
return len(self.status.get('services', {})) |
|
1813 |
||
966.1.10
by John George
Check that subordinate unit count matches service unit count in wait_for_subordinate_units. |
1814 |
def get_service_unit_count(self, service): |
1815 |
return len( |
|
1816 |
self.status.get('services', {}).get(service, {}).get('units', {})) |
|
1817 |
||
84.1.4
by Aaron Bentley
Move version-dicting to jujupy. |
1818 |
def get_agent_versions(self): |
1819 |
versions = defaultdict(set) |
|
1820 |
for item_name, item in self.agent_items(): |
|
1294
by Curtis Hovey
Get machine version from juju-status. |
1821 |
if item.get('juju-status', None): |
1822 |
version = item['juju-status'].get('version', 'unknown') |
|
1823 |
versions[version].add(item_name) |
|
1824 |
else: |
|
1825 |
versions[item.get('agent-version', 'unknown')].add(item_name) |
|
84.1.4
by Aaron Bentley
Move version-dicting to jujupy. |
1826 |
return versions |
1827 |
||
717.2.2
by Aaron Bentley
Checkpoint with assess_recovery working. |
1828 |
def get_instance_id(self, machine_id): |
1829 |
return self.status['machines'][machine_id]['instance-id'] |
|
1830 |
||
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1831 |
def get_unit(self, unit_name): |
874.2.8
by Aaron Bentley
Add docs, simplify get_unit. |
1832 |
"""Return metadata about a unit."""
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1833 |
for service in sorted(self.status['services'].values()): |
874.2.8
by Aaron Bentley
Add docs, simplify get_unit. |
1834 |
if unit_name in service.get('units', {}): |
1835 |
return service['units'][unit_name] |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1836 |
raise KeyError(unit_name) |
1837 |
||
966.1.9
by John George
Add wait_for_subordinate_unit to jujupy.py. |
1838 |
def service_subordinate_units(self, service_name): |
966.1.1
by John George
Add get_subordinate_units() to jujupy.Status. |
1839 |
"""Return subordinate metadata for a service_name."""
|
1840 |
services = self.status.get('services', {}) |
|
966.1.12
by John George
Verify subordinate units are started in wait_for_subordinate_units and add the reporter. |
1841 |
if service_name in services: |
966.1.1
by John George
Add get_subordinate_units() to jujupy.Status. |
1842 |
for unit in sorted(services[service_name].get( |
1843 |
'units', {}).values()): |
|
966.1.9
by John George
Add wait_for_subordinate_unit to jujupy.py. |
1844 |
for sub_name, sub in unit.get('subordinates', {}).items(): |
1845 |
yield sub_name, sub |
|
966.1.1
by John George
Add get_subordinate_units() to jujupy.Status. |
1846 |
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1847 |
def get_open_ports(self, unit_name): |
874.2.8
by Aaron Bentley
Add docs, simplify get_unit. |
1848 |
"""List the open ports for the specified unit.
|
1849 |
||
1850 |
If no ports are listed for the unit, the empty list is returned.
|
|
1851 |
"""
|
|
874.2.4
by Aaron Bentley
Initial upgrade-charm test. |
1852 |
return self.get_unit(unit_name).get('open-ports', []) |
1853 |
||
1854 |
||
657.1.3
by Aaron Bentley
Extract SimpleEnvironment from Environment. |
1855 |
class SimpleEnvironment: |
2
by Aaron Bentley
Added initial deploy_stack. |
1856 |
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
1857 |
def __init__(self, environment, config=None, juju_home=None, |
1858 |
controller=None): |
|
1859 |
if controller is None: |
|
1860 |
controller = Controller(environment) |
|
1861 |
self.controller = controller |
|
2
by Aaron Bentley
Added initial deploy_stack. |
1862 |
self.environment = environment |
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
1863 |
self.config = config |
1193.2.1
by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home. |
1864 |
self.juju_home = juju_home |
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
1865 |
if self.config is not None: |
1866 |
self.local = bool(self.config.get('type') == 'local') |
|
530.1.1
by Curtis Hovey
Extend the timeout to retrieve the dummy source token |
1867 |
self.kvm = ( |
1868 |
self.local and bool(self.config.get('container') == 'kvm')) |
|
667.1.1
by John George
Add amd64 contraint, when bootstrapping the maas test environment. |
1869 |
self.maas = bool(self.config.get('type') == 'maas') |
940.2.1
by Martin Packman
Only use kvm packages on joyent provider |
1870 |
self.joyent = bool(self.config.get('type') == 'joyent') |
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
1871 |
else: |
1872 |
self.local = False |
|
940.2.1
by Martin Packman
Only use kvm packages on joyent provider |
1873 |
self.kvm = False |
667.1.1
by John George
Add amd64 contraint, when bootstrapping the maas test environment. |
1874 |
self.maas = False |
940.2.1
by Martin Packman
Only use kvm packages on joyent provider |
1875 |
self.joyent = False |
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
1876 |
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
1877 |
def clone(self, model_name=None): |
1878 |
config = deepcopy(self.config) |
|
1879 |
if model_name is None: |
|
1880 |
model_name = self.environment |
|
1881 |
else: |
|
1882 |
config['name'] = model_name |
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
1883 |
result = self.__class__(model_name, config, self.juju_home, |
1884 |
self.controller) |
|
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
1885 |
result.local = self.local |
1886 |
result.kvm = self.kvm |
|
1887 |
result.maas = self.maas |
|
1888 |
result.joyent = self.joyent |
|
1889 |
return result |
|
1890 |
||
696.1.7
by Aaron Bentley
Get industrial_test working at the commandline. |
1891 |
def __eq__(self, other): |
1892 |
if type(self) != type(other): |
|
1893 |
return False |
|
1894 |
if self.environment != other.environment: |
|
1895 |
return False |
|
1896 |
if self.config != other.config: |
|
1897 |
return False |
|
1898 |
if self.local != other.local: |
|
1899 |
return False |
|
1900 |
if self.maas != other.maas: |
|
1901 |
return False |
|
1902 |
return True |
|
1903 |
||
1904 |
def __ne__(self, other): |
|
1905 |
return not self == other |
|
1906 |
||
1315.2.23
by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap. |
1907 |
def set_model_name(self, model_name, set_controller=True): |
1908 |
if set_controller: |
|
1909 |
self.controller.name = model_name |
|
1910 |
self.environment = model_name |
|
1911 |
self.config['name'] = model_name |
|
1912 |
||
112.1.4
by Aaron Bentley
Read config, use it to determine whether provider is local. |
1913 |
@classmethod
|
1914 |
def from_config(cls, name): |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1915 |
return cls._from_config(name) |
1916 |
||
1917 |
@classmethod
|
|
1918 |
def _from_config(cls, name): |
|
1082.1.1
by Aaron Bentley
SimpleEnvironment.from_config env_name can be None. |
1919 |
config, selected = get_selected_environment(name) |
1920 |
if name is None: |
|
1921 |
name = selected |
|
1922 |
return cls(name, config) |
|
657.1.3
by Aaron Bentley
Extract SimpleEnvironment from Environment. |
1923 |
|
1924 |
def needs_sudo(self): |
|
1925 |
return self.local |
|
1926 |
||
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1927 |
@contextmanager
|
1928 |
def make_jes_home(self, juju_home, dir_name, new_config): |
|
1929 |
home_path = jes_home_path(juju_home, dir_name) |
|
1930 |
if os.path.exists(home_path): |
|
1931 |
rmtree(home_path) |
|
1932 |
os.makedirs(home_path) |
|
1260.2.25
by Aaron Bentley
Test load/dump yaml. |
1933 |
self.dump_yaml(home_path, new_config) |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1934 |
yield home_path |
1935 |
||
1260.2.25
by Aaron Bentley
Test load/dump yaml. |
1936 |
def dump_yaml(self, path, config): |
1937 |
dump_environments_yaml(path, config) |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1938 |
|
1939 |
||
1940 |
class JujuData(SimpleEnvironment): |
|
1941 |
||
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
1942 |
def __init__(self, environment, config=None, juju_home=None, |
1943 |
controller=None): |
|
1260.2.31
by Aaron Bentley
Restore get_juju_home. |
1944 |
if juju_home is None: |
1945 |
juju_home = get_juju_home() |
|
1315.2.22
by Aaron Bentley
Fake merge of trunk. |
1946 |
super(JujuData, self).__init__(environment, config, juju_home, |
1947 |
controller) |
|
1281.1.1
by Aaron Bentley
Rollback r1280 per Ian's email. |
1948 |
self.credentials = {} |
1949 |
self.clouds = {} |
|
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1950 |
|
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
1951 |
def clone(self, model_name=None): |
1315.2.8
by Aaron Bentley
Dump logs for all models, including on JES. |
1952 |
result = super(JujuData, self).clone(model_name) |
1315.2.3
by Aaron Bentley
Use admin client to get bootstrap host. |
1953 |
result.credentials = deepcopy(self.credentials) |
1954 |
result.clouds = deepcopy(self.clouds) |
|
1955 |
return result |
|
1956 |
||
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1957 |
@classmethod
|
1958 |
def from_env(cls, env): |
|
1959 |
juju_data = cls(env.environment, env.config, env.juju_home) |
|
1960 |
juju_data.load_yaml() |
|
1961 |
return juju_data |
|
1962 |
||
1963 |
def load_yaml(self): |
|
1964 |
with open(os.path.join(self.juju_home, 'credentials.yaml')) as f: |
|
1965 |
self.credentials = yaml.safe_load(f) |
|
1966 |
with open(os.path.join(self.juju_home, 'clouds.yaml')) as f: |
|
1967 |
self.clouds = yaml.safe_load(f) |
|
1968 |
||
1969 |
@classmethod
|
|
1970 |
def from_config(cls, name): |
|
1971 |
juju_data = cls._from_config(name) |
|
1972 |
juju_data.load_yaml() |
|
1973 |
return juju_data |
|
1974 |
||
1260.2.25
by Aaron Bentley
Test load/dump yaml. |
1975 |
def dump_yaml(self, path, config): |
1260.2.30
by Aaron Bentley
Updates from review |
1976 |
"""Dump the configuration files to the specified path.
|
1977 |
||
1978 |
config is unused, but is accepted for compatibility with
|
|
1979 |
SimpleEnvironment and make_jes_home().
|
|
1980 |
"""
|
|
1260.2.25
by Aaron Bentley
Test load/dump yaml. |
1981 |
with open(os.path.join(path, 'credentials.yaml'), 'w') as f: |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1982 |
yaml.safe_dump(self.credentials, f) |
1260.2.25
by Aaron Bentley
Test load/dump yaml. |
1983 |
with open(os.path.join(path, 'clouds.yaml'), 'w') as f: |
1260.2.6
by Aaron Bentley
Introduce JujuData, remove workarounds for cloud-credentials. |
1984 |
yaml.safe_dump(self.clouds, f) |
1985 |
||
1260.2.24
by Aaron Bentley
Move cloud/location to JujuData, add tests, fix lint. |
1986 |
def find_endpoint_cloud(self, cloud_type, endpoint): |
1987 |
for cloud, cloud_config in self.clouds['clouds'].items(): |
|
1988 |
if cloud_config['type'] != cloud_type: |
|
1989 |
continue
|
|
1990 |
if cloud_config['endpoint'] == endpoint: |
|
1991 |
return cloud |
|
1992 |
raise LookupError('No such endpoint: {}'.format(endpoint)) |
|
1993 |
||
1994 |
def get_cloud(self): |
|
1995 |
provider = self.config['type'] |
|
1996 |
# Separate cloud recommended by: Juju Cloud / Credentials / BootStrap /
|
|
1997 |
# Model CLI specification
|
|
1998 |
if provider == 'ec2' and self.config['region'] == 'cn-north-1': |
|
1999 |
return 'aws-china' |
|
2000 |
if provider not in ('maas', 'openstack'): |
|
2001 |
return { |
|
2002 |
'ec2': 'aws', |
|
2003 |
'gce': 'google', |
|
2004 |
}.get(provider, provider) |
|
2005 |
if provider == 'maas': |
|
2006 |
endpoint = self.config['maas-server'] |
|
2007 |
elif provider == 'openstack': |
|
2008 |
endpoint = self.config['auth-url'] |
|
2009 |
return self.find_endpoint_cloud(provider, endpoint) |
|
2010 |
||
2011 |
def get_region(self): |
|
2012 |
provider = self.config['type'] |
|
2013 |
if provider == 'azure': |
|
2014 |
if 'tenant-id' not in self.config: |
|
2015 |
raise ValueError('Non-ARM Azure not supported.') |
|
2016 |
return self.config['location'] |
|
2017 |
elif provider == 'joyent': |
|
2018 |
matcher = re.compile('https://(.*).api.joyentcloud.com') |
|
2019 |
return matcher.match(self.config['sdc-url']).group(1) |
|
2020 |
elif provider == 'lxd': |
|
2021 |
return 'localhost' |
|
1280.1.1
by Aaron Bentley
Supply bootstrap host as manual region. |
2022 |
elif provider == 'manual': |
2023 |
return self.config['bootstrap-host'] |
|
1260.2.24
by Aaron Bentley
Move cloud/location to JujuData, add tests, fix lint. |
2024 |
elif provider in ('maas', 'manual'): |
2025 |
return None |
|
2026 |
else: |
|
2027 |
return self.config['region'] |
|
2028 |
||
657.1.3
by Aaron Bentley
Extract SimpleEnvironment from Environment. |
2029 |
|
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2030 |
class GroupReporter: |
2031 |
||
2032 |
def __init__(self, stream, expected): |
|
2033 |
self.stream = stream |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
2034 |
self.expected = expected |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2035 |
self.last_group = None |
2036 |
self.ticks = 0 |
|
922.1.1
by Martin Packman
Add wrapping at 80 characters to group reporter |
2037 |
self.wrap_offset = 0 |
922.1.2
by Martin Packman
Use width of 79 and add test suggested by abentley in review |
2038 |
self.wrap_width = 79 |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2039 |
|
2040 |
def _write(self, string): |
|
2041 |
self.stream.write(string) |
|
2042 |
self.stream.flush() |
|
2043 |
||
769.1.1
by Martin Packman
Add finish method to GroupReporter to end dotties |
2044 |
def finish(self): |
2045 |
if self.last_group: |
|
2046 |
self._write("\n") |
|
2047 |
||
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2048 |
def update(self, group): |
2049 |
if group == self.last_group: |
|
922.1.1
by Martin Packman
Add wrapping at 80 characters to group reporter |
2050 |
if (self.wrap_offset + self.ticks) % self.wrap_width == 0: |
2051 |
self._write("\n") |
|
2052 |
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 |
2053 |
self.ticks += 1 |
2054 |
return
|
|
2055 |
value_listing = [] |
|
2056 |
for value, entries in sorted(group.items()): |
|
1196.1.6
by Martin Packman
Changes for review by abentley |
2057 |
if value == self.expected: |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2058 |
continue
|
2059 |
value_listing.append('%s: %s' % (value, ', '.join(entries))) |
|
2060 |
string = ' | '.join(value_listing) |
|
930.1.1
by Martin Packman
Fix wrapping of dots when printing updated group status |
2061 |
lead_length = len(string) + 1 |
751.3.1
by Martin Packman
Use dots rather than repeated lines when waiting for status changes |
2062 |
if self.last_group: |
2063 |
string = "\n" + string |
|
2064 |
self._write(string) |
|
2065 |
self.last_group = group |
|
2066 |
self.ticks = 0 |
|
922.1.1
by Martin Packman
Add wrapping at 80 characters to group reporter |
2067 |
self.wrap_offset = lead_length if lead_length < self.wrap_width else 0 |