4
from .base import BaseAction
5
from ..relation import EndpointPair
6
from ..utils import _parse_constraints, yaml_dump
9
class Diff(BaseAction):
11
log = logging.getLogger("deployer.diff")
13
def __init__(self, env, deployment, options):
14
self.options = options
16
self.deployment = deployment
17
self.env_status = None
18
self.env_state = {'services': {}, 'relations': []}
24
for svc_name in self.env_status['services']:
25
if not svc_name in self.env_status['services']:
26
self.env_state['services'][svc_name] = 'missing'
27
self.env_state['services'].setdefault(svc_name, {})[
28
'options'] = self.env.get_config(svc_name)
29
self.env_state['services'][svc_name][
30
'constraints'] = self.env.get_constraints(svc_name)
31
self.env_state['services'][svc_name][
32
'unit_count'] = len(self.env_status[
33
'services'][svc_name]['units'])
34
rels.update(self._load_rels(svc_name))
35
self.env_state['relations'] = sorted(rels)
37
def _load_rels(self, svc_name):
39
svc_rels = self.env_status['services'][svc_name].get(
41
# There is ambiguity here for multiple rels between two
42
# services without the relation id, which we need support
44
for r_name, r_svcs in svc_rels.items():
49
rr_name = self._get_rel_name(svc_name, r_svc)
52
"%s:%s" % (svc_name, r_name),
53
"%s:%s" % (r_svc, rr_name)])))
56
def _get_rel_name(self, src, tgt):
57
svc_rels = self.env_status['services'][tgt]['relations']
59
for r, eps in svc_rels.items():
62
raise ValueError("Ambigious relations for service")
68
rels_delta = self._get_relations_delta()
70
delta['relations'] = rels_delta
71
svc_delta = self._get_services_delta()
73
delta['services'] = svc_delta
76
def _get_relations_delta(self):
77
# Simple endpoint diff, no qualified endpoint checking.
79
# Env relations are always qualified (at least in go).
82
EndpointPair(*x) for x in self.env_state.get('relations', ()))
84
[EndpointPair(*y) for y in self.deployment.get_relations()])
86
for r in dep_rels.difference(env_rels):
87
delta.setdefault('missing', []).append(r)
89
for r in env_rels.difference(dep_rels):
90
delta.setdefault('unknown', []).append(r)
94
def _get_services_delta(self):
96
env_svcs = set(self.env_status['services'].keys())
97
dep_svcs = set([s.name for s in self.deployment.get_services()])
99
missing = dep_svcs - env_svcs
101
delta['missing'] = {}
103
delta['missing'][a] = self.deployment.get_service(
105
unknown = env_svcs - dep_svcs
107
delta['unknown'] = {}
109
delta['unknown'][r] = self.env_state.get(r)
111
for cs in env_svcs.intersection(dep_svcs):
112
d_s = self.deployment.get_service(cs).svc_data
113
e_s = self.env_state['services'][cs]
114
mod = self._diff_service(e_s, d_s)
117
if not 'modified' in delta:
118
delta['modified'] = {}
119
delta['modified'][cs] = mod
122
def _diff_service(self, e_s, d_s):
124
if 'constraints' in d_s:
125
d_sc = _parse_constraints(d_s['constraints'])
126
if d_sc != e_s['constraints']:
127
mod['constraints'] = e_s['constraints']
128
for k, v in d_s.get('options', {}).items():
129
# Deploy options not known to the env may originate
130
# from charm version delta or be an invalid config.
131
if not k in e_s['options']:
133
e_v = e_s['options'].get(k, {}).get('value')
135
mod['config'] = {k: e_v}
136
if e_s['unit_count'] != d_s.get('num_units', 1):
137
mod['num_units'] = e_s['num_units']
141
self.start_time = time.time()
143
self.env_status = self.env.status()
145
delta = self.get_delta()
147
print yaml_dump(delta)