5
from .base import BaseEnvironment
6
from ..utils import ErrorExit
8
from jujuclient import Environment as EnvironmentClient, UnitErrors, EnvError
11
class GoEnvironment(BaseEnvironment):
13
def __init__(self, name, options=None, endpoint=None):
15
self.options = options
16
self.api_endpoint = endpoint
20
config = self._get_env_config()
21
return config['admin-secret']
23
def add_units(self, service_name, num_units):
24
return self.client.add_units(service_name, num_units)
26
def add_relation(self, endpoint_a, endpoint_b):
27
return self.client.add_relation(endpoint_a, endpoint_b)
34
if not self.api_endpoint:
35
# Should really have a cheaper/faster way of getting the endpoint
36
# ala juju status 0 for a get_endpoint method.
40
self.client = EnvironmentClient(self.api_endpoint)
41
except socket.error, e:
42
if e.errno != errno.ETIMEDOUT:
47
self.client.login(self._get_token())
48
self.log.debug("Connected to environment")
50
def get_config(self, svc_name):
51
return self.client.get_config(svc_name)
53
def get_constraints(self, svc_name):
54
return self.client.get_constraints(svc_name)
56
def get_cli_status(self):
57
status = super(GoEnvironment, self).get_cli_status()
58
# Opportunistic, see connect method comment.
59
if not self.api_endpoint:
60
self.api_endpoint = "wss://%s:17070/" % (
61
status["machines"]["0"]["dns-name"])
65
terminate_machines=False,
67
timeout=360, watch=False):
68
"""Destroy/reset the environment."""
69
status = self.status()
71
for s in status.get('services', {}).keys():
72
self.log.debug(" Destroying service %s", s)
73
self.client.destroy_service(s)
77
# Mark any errors as resolved so destruction can proceed.
81
self.wait_for_units(timeout, "removed", watch=watch)
83
# The only value to not terminating is keeping the data on the
85
if not terminate_machines:
87
" warning: juju-core machines are not reusable for units")
89
self._terminate_machines(status, watch, terminate_delay)
91
def _terminate_machines(self, status, watch, terminate_wait):
92
"""Terminate all machines, optionally wait for termination.
95
self.log.debug(" Terminating machines")
97
# Don't bother if there are no service unit machines
98
if len(status['machines']) == 1:
101
for mid in status['machines'].keys():
104
self.log.debug(" Terminating machine %s", mid)
105
self.terminate_machine(mid)
108
self.log.info(" Waiting for machine termination")
109
callback = watch and self._delta_event_log or None
110
self.client.wait_for_no_machines(None, callback)
112
def _check_timeout(self, etime):
113
w_timeout = etime - time.time()
115
self.log.error("Timeout reached while resolving errors")
119
def resolve_errors(self, retry_count=0, timeout=600, watch=False, delay=5):
120
"""Resolve any unit errors in the environment.
122
If retry_count is given then the hook execution is reattempted. The
123
system will do up to retry_count passes through the system resolving
126
If retry count is not given, the error is marked resolved permanently.
128
etime = time.time() + timeout
131
error_units = self._get_units_in_error()
132
for e_uid in error_units:
134
self.client.resolved(e_uid, retry=bool(retry_count))
135
self.log.debug(" Resolving error on %s", e_uid)
137
if 'already resolved' in e:
140
if not error_units and count:
142
self.log.debug(" No unit errors found.")
144
self.log.debug(" No more unit errors found.")
147
w_timeout = self._check_timeout(etime)
154
timeout=int(w_timeout), watch=True, no_exit=True)
155
except UnitErrors, e:
156
if retry_count == count:
158
" Retry count %d exhausted, but units in error (%s)",
159
retry_count, " ".join(u['Name'] for u in e.errors))
165
return self.client.get_stat()
168
self, timeout, goal_state="started", watch=False, no_exit=False):
169
"""Wait for units to reach a given condition.
171
callback = watch and self._delta_event_log or None
173
self.client.wait_for_units(timeout, goal_state, callback=callback)
174
except UnitErrors, e:
176
"unit: %s: machine: %s agent-state: %s details: %s" % (
177
u['Name'], u['MachineId'], u['Status'], u['StatusInfo']
182
self.log.error("The following units had errors:\n %s" % (
183
" \n".join(error_units)))
186
def _delta_event_log(self, et, ct, d):
187
# event type, change type, data
188
name = d.get('Name', d.get('Id', 'unknown'))
189
state = d.get('Status', d.get('Life', 'unknown'))
191
name = self._format_endpoints(d['Endpoints'])
196
" Delta %s: %s %s:%s", et, name, ct, state)
198
def _format_endpoints(self, eps):
201
return "[%s:%s:%s]" % (
203
ep['Relation']['Name'],
204
ep['Relation']['Role'])
206
return "[%s:%s <-> %s:%s]" % (
207
eps[0]['ServiceName'],
208
eps[0]['Relation']['Name'],
209
eps[1]['ServiceName'],
210
eps[1]['Relation']['Name'])