979.3.1
by Horacio Durán
Added CI tests for status. |
1 |
from __future__ import print_function |
979.3.3
by Horacio Durán
Proper python formatting. |
2 |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
3 |
import json |
4 |
import re |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
5 |
from unittest import FunctionTestCase |
6 |
||
979.3.1
by Horacio Durán
Added CI tests for status. |
7 |
from jujupy import yaml_loads |
8 |
||
1092.2.2
by Aaron Bentley
Fix lint. |
9 |
|
10 |
__metaclass__ = type |
|
11 |
||
12 |
||
979.3.3
by Horacio Durán
Proper python formatting. |
13 |
# Machine and unit, deprecated in unit
|
14 |
AGENT_STATE_KEY = "agent-state" |
|
15 |
AGENT_VERSION_KEY = "agent-version" |
|
16 |
DNS_NAME_KEY = "dns-name" |
|
17 |
INSTANCE_ID_KEY = "instance-id" |
|
18 |
HARDWARE_KEY = "hardware" |
|
19 |
SERIES_KEY = "series" |
|
20 |
STATE_SERVER_MEMBER_STATUS_KEY = "state-server-member-status" |
|
21 |
# service
|
|
22 |
CHARM_KEY = "charm" |
|
23 |
EXPOSED_KEY = "exposed" |
|
24 |
SERVICE_STATUS_KEY = "service-status" |
|
25 |
# unit
|
|
26 |
WORKLOAD_STATUS_KEY = "workload-status" |
|
27 |
AGENT_STATUS_KEY = "agent-status" |
|
28 |
MACHINE_KEY = "machine" |
|
29 |
PUBLIC_ADDRESS_KEY = "public-address" |
|
30 |
||
31 |
MACHINE_TAB_HEADERS = ['ID', 'STATE', 'VERSION', 'DNS', 'INS-ID', 'SERIES', |
|
32 |
'HARDWARE'] |
|
33 |
UNIT_TAB_HEADERS = ['ID', 'WORKLOAD-STATE', 'AGENT-STATE', 'VERSION', |
|
34 |
'MACHINE', 'PORTS', 'PUBLIC-ADDRESS', 'MESSAGE'] |
|
35 |
SERVICE_TAB_HEADERS = ['NAME', 'STATUS', 'EXPOSED', 'CHARM'] |
|
36 |
||
979.3.1
by Horacio Durán
Added CI tests for status. |
37 |
|
38 |
class StatusTester: |
|
39 |
def __init__(self, text="", status_format="yaml"): |
|
40 |
self._text = text |
|
41 |
self._format = status_format |
|
42 |
self.s = globals()["Status%sParser" % status_format.capitalize()](text) |
|
43 |
||
44 |
@classmethod
|
|
45 |
def from_text(cls, text, status_format): |
|
46 |
return cls(text, status_format) |
|
47 |
||
48 |
def __unicode__(self): |
|
49 |
return self._text |
|
50 |
||
51 |
def __str__(self): |
|
52 |
return self._text |
|
53 |
||
979.3.3
by Horacio Durán
Proper python formatting. |
54 |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
55 |
class ErrNoStatus(Exception): |
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
56 |
"""An exception for missing juju status."""
|
979.3.1
by Horacio Durán
Added CI tests for status. |
57 |
|
979.3.3
by Horacio Durán
Proper python formatting. |
58 |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
59 |
class ErrMalformedStatus(Exception): |
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
60 |
"""An exception for unexpected formats of status."""
|
979.3.1
by Horacio Durán
Added CI tests for status. |
61 |
|
979.3.3
by Horacio Durán
Proper python formatting. |
62 |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
63 |
class ErrUntestedStatusOutput(Exception): |
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
64 |
"""An exception for results returned by status.
|
65 |
||
66 |
Status that are known not to be covered by the tests should raise
|
|
67 |
this exception. """
|
|
979.3.1
by Horacio Durán
Added CI tests for status. |
68 |
|
69 |
||
979.3.3
by Horacio Durán
Proper python formatting. |
70 |
class BaseStatusParser: |
979.3.1
by Horacio Durán
Added CI tests for status. |
71 |
|
72 |
_expected = set(["environment", "services", "machines"]) |
|
73 |
||
74 |
def __init__(self): |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
75 |
self.tc = FunctionTestCase("", description=self.__class__.__name__) |
979.3.3
by Horacio Durán
Proper python formatting. |
76 |
# expected entity storage
|
979.3.1
by Horacio Durán
Added CI tests for status. |
77 |
self._machines = dict() |
78 |
self._services = dict() |
|
79 |
self._units = dict() |
|
80 |
||
81 |
self.parse() |
|
82 |
||
83 |
def parse(self): |
|
84 |
return self._parse() |
|
85 |
||
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
86 |
def store(self, parsed): |
979.3.5
by Horacio Durán
Addressed curtis observations. |
87 |
# Either there are less items than expected and therefore status
|
88 |
# is returning a subset of what it should or there are more and
|
|
89 |
# this means there are untested keys in status.
|
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
90 |
self.tc.assertItemsEqual(parsed.keys(), self._expected, |
91 |
"untested items or incomplete status output") |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
92 |
|
93 |
# status of machines.
|
|
94 |
for machine_id, machine in parsed.get("machines", {}).iteritems(): |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
95 |
self.tc.assertNotIn(machine_id, self._machines, |
96 |
"Machine %s is repeated in yaml" |
|
97 |
" status " % machine_id) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
98 |
self._machines[machine_id] = machine |
99 |
||
100 |
# status of services
|
|
101 |
for service_name, service in parsed.get("services", {}).iteritems(): |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
102 |
self.tc.assertNotIn(service_name, self._services, |
103 |
"Service %s is repeated in yaml " |
|
104 |
"status " % service_name) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
105 |
self._services[service_name] = service |
106 |
||
107 |
# status of units
|
|
108 |
for service_name, service in self._services.iteritems(): |
|
109 |
for unit_name, unit in service.get("units", {}).iteritems(): |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
110 |
self.tc.assertNotIn(unit_name, self._units, |
111 |
"Unit %s is repeated in yaml " |
|
112 |
"status " % unit_name) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
113 |
self._units[unit_name] = unit |
114 |
||
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
115 |
def assert_machines_len(self, expected_len): |
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
116 |
"""Assert that we got as many machines as we where expecting.
|
979.3.1
by Horacio Durán
Added CI tests for status. |
117 |
|
118 |
:param expected_len: expected quantity of machines.
|
|
119 |
:type expected_len: int
|
|
120 |
"""
|
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
121 |
self.tc.assertEqual(len(self._machines), expected_len) |
979.3.1
by Horacio Durán
Added CI tests for status. |
122 |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
123 |
def assert_machines_ids(self, expected_ids): |
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
124 |
"""Assert that we got the machines we where expecting.
|
979.3.1
by Horacio Durán
Added CI tests for status. |
125 |
|
126 |
:param expected_ids: expected ids of machines.
|
|
127 |
:type expected_ids: tuple
|
|
128 |
"""
|
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
129 |
self.tc.assertItemsEqual(self._machines, expected_ids) |
979.3.5
by Horacio Durán
Addressed curtis observations. |
130 |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
131 |
def _machine_key_get(self, machine_id, key): |
132 |
self.tc.assertIn(machine_id, self._machines, |
|
133 |
"Machine \"%s\" not present in machines" % machine_id) |
|
134 |
self.tc.assertIn(key, self._machines[machine_id], |
|
135 |
"Key \"%s\" not present in Machine \"%s\"" % |
|
136 |
(key, machine_id)) |
|
979.3.5
by Horacio Durán
Addressed curtis observations. |
137 |
return self._machines[machine_id][key] |
138 |
||
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
139 |
def assert_machine_agent_state(self, machine_id, state): |
140 |
value = self._machine_key_get(machine_id, AGENT_STATE_KEY) |
|
141 |
self.tc.assertEqual(value, state) |
|
142 |
||
143 |
def assert_machine_agent_version(self, machine_id, version): |
|
144 |
value = self._machine_key_get(machine_id, AGENT_VERSION_KEY) |
|
145 |
self.tc.assertEqual(value, version) |
|
146 |
||
147 |
def assert_machine_dns_name(self, machine_id, dns_name): |
|
148 |
value = self._machine_key_get(machine_id, DNS_NAME_KEY) |
|
149 |
self.tc.assertEqual(value, dns_name) |
|
150 |
||
151 |
def assert_machine_instance_id(self, machine_id, instance_id): |
|
152 |
value = self._machine_key_get(machine_id, INSTANCE_ID_KEY) |
|
153 |
self.tc.assertEqual(value, instance_id) |
|
154 |
||
155 |
def assert_machine_series(self, machine_id, series): |
|
156 |
value = self._machine_key_get(machine_id, SERIES_KEY) |
|
157 |
self.tc.assertEqual(value, series) |
|
158 |
||
159 |
def assert_machine_hardware(self, machine_id, hardware): |
|
160 |
value = self._machine_key_get(machine_id, HARDWARE_KEY) |
|
161 |
self.tc.assertEqual(value, hardware) |
|
162 |
||
163 |
def assert_machine_member_status(self, machine_id, member_status): |
|
164 |
value = self._machine_key_get(machine_id, |
|
979.3.5
by Horacio Durán
Addressed curtis observations. |
165 |
STATE_SERVER_MEMBER_STATUS_KEY) |
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
166 |
self.tc.assertEqual(value, member_status) |
979.3.5
by Horacio Durán
Addressed curtis observations. |
167 |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
168 |
def _service_key_get(self, service_name, key): |
169 |
self.tc.assertIn(service_name, self._services, |
|
170 |
"Service \"%s\" not present in services." % |
|
171 |
service_name) |
|
172 |
self.tc.assertIn(key, self._services[service_name], |
|
173 |
"Key \"%s\" not present in Service \"%s\"" % |
|
174 |
(key, service_name)) |
|
979.3.5
by Horacio Durán
Addressed curtis observations. |
175 |
return self._services[service_name][key] |
979.3.1
by Horacio Durán
Added CI tests for status. |
176 |
|
979.3.3
by Horacio Durán
Proper python formatting. |
177 |
# Service status
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
178 |
def assert_service_charm(self, service_name, charm): |
179 |
value = self._service_key_get(service_name, CHARM_KEY) |
|
180 |
self.tc.assertEqual(value, charm) |
|
181 |
||
182 |
def assert_service_exposed(self, service_name, exposed): |
|
183 |
value = self._service_key_get(service_name, EXPOSED_KEY) |
|
184 |
self.tc.assertEqual(value, exposed) |
|
185 |
||
186 |
def assert_service_service_status(self, service_name, |
|
979.3.3
by Horacio Durán
Proper python formatting. |
187 |
status={"current": "", "message": ""}): |
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
188 |
value = self._service_key_get(service_name, SERVICE_STATUS_KEY) |
189 |
self.tc.assertEqual(value["current"], status["current"]) |
|
190 |
self.tc.assertEqual(value["message"], status["message"]) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
191 |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
192 |
def _unit_key_get(self, unit_name, key): |
193 |
self.tc.assertIn(unit_name, self._units, |
|
194 |
"Unit \"%s\" not present in units" % unit_name) |
|
195 |
self.tc.assertIn(key, self._units[unit_name], |
|
196 |
"Key \"%s\" not present in Unit \"%s\"" % |
|
197 |
(key, unit_name)) |
|
979.3.5
by Horacio Durán
Addressed curtis observations. |
198 |
return self._units[unit_name][key] |
979.3.1
by Horacio Durán
Added CI tests for status. |
199 |
|
979.3.3
by Horacio Durán
Proper python formatting. |
200 |
# Units status
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
201 |
def assert_unit_workload_status(self, unit_name, |
979.3.3
by Horacio Durán
Proper python formatting. |
202 |
status={"current": "", "message": ""}): |
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
203 |
value = self._unit_key_get(unit_name, WORKLOAD_STATUS_KEY) |
204 |
self.tc.assertEqual(value["current"], status["current"]) |
|
205 |
self.tc.assertEqual(value["message"], status["message"]) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
206 |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
207 |
def assert_unit_agent_status(self, unit_name, |
979.3.3
by Horacio Durán
Proper python formatting. |
208 |
status={"current": "", "message": ""}): |
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
209 |
value = self._unit_key_get(unit_name, AGENT_STATUS_KEY) |
210 |
self.tc.assertEqual(value["current"], status["current"]) |
|
979.3.5
by Horacio Durán
Addressed curtis observations. |
211 |
# Message is optional for unit agents.
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
212 |
self.tc.assertEqual(value.get("message", ""), status["message"]) |
213 |
||
214 |
def assert_unit_agent_state(self, unit_name, state): |
|
215 |
value = self._unit_key_get(unit_name, AGENT_STATE_KEY) |
|
216 |
self.tc.assertEqual(value, state) |
|
217 |
||
218 |
def assert_unit_agent_version(self, unit_name, version): |
|
219 |
value = self._unit_key_get(unit_name, AGENT_VERSION_KEY) |
|
220 |
self.tc.assertEqual(value, version) |
|
221 |
||
222 |
def assert_unit_machine(self, unit_name, machine): |
|
223 |
value = self._unit_key_get(unit_name, MACHINE_KEY) |
|
224 |
self.tc.assertEqual(value, machine) |
|
225 |
||
226 |
def assert_unit_public_address(self, unit_name, address): |
|
227 |
value = self._unit_key_get(unit_name, PUBLIC_ADDRESS_KEY) |
|
228 |
self.tc.assertEqual(value, address) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
229 |
|
230 |
||
231 |
class StatusYamlParser(BaseStatusParser): |
|
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
232 |
"""StatusYamlParser handles parsing of status output in yaml format.
|
233 |
||
234 |
To be used by status tester.
|
|
979.3.1
by Horacio Durán
Added CI tests for status. |
235 |
"""
|
236 |
||
237 |
def __init__(self, yaml=""): |
|
238 |
self._yaml = yaml |
|
239 |
if yaml == "": |
|
240 |
raise ErrNoStatus("Yaml status was empty") |
|
241 |
super(StatusYamlParser, self).__init__() |
|
242 |
||
243 |
def _parse(self): |
|
244 |
parsed = yaml_loads(self._yaml) |
|
245 |
self.store(parsed) |
|
246 |
||
247 |
||
248 |
class StatusJsonParser(BaseStatusParser): |
|
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
249 |
"""StatusJSONParser handles parsing of status output in JSON format.
|
250 |
||
251 |
To be used by status tester.
|
|
979.3.1
by Horacio Durán
Added CI tests for status. |
252 |
"""
|
253 |
||
254 |
def __init__(self, json_text=""): |
|
255 |
self._json = json_text |
|
256 |
if json_text == "": |
|
257 |
raise ErrNoStatus("JSON status was empty") |
|
258 |
super(StatusJsonParser, self).__init__() |
|
259 |
||
260 |
def _parse(self): |
|
261 |
parsed = json.loads(self._json) |
|
262 |
self.store(parsed) |
|
263 |
||
264 |
||
265 |
class StatusTabularParser(BaseStatusParser): |
|
979.3.6
by Horacio Durán
Addressed extra comments from Curtis. |
266 |
"""StatusTabularParser handles parsing of status output in Tabular format.
|
267 |
||
268 |
To be used by status tester.
|
|
979.3.1
by Horacio Durán
Added CI tests for status. |
269 |
"""
|
270 |
||
271 |
def __init__(self, tabular_text=""): |
|
272 |
self._tabular = tabular_text |
|
273 |
if tabular_text == "": |
|
274 |
raise ErrNoStatus("tabular status was empty") |
|
275 |
super(StatusTabularParser, self).__init__() |
|
276 |
||
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
277 |
def _normalize_machines(self, header, items): |
979.3.1
by Horacio Durán
Added CI tests for status. |
278 |
nitems = items[:6] |
279 |
nitems.append(" ".join(items[6:])) |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
280 |
self.tc.assertEqual(header, MACHINE_TAB_HEADERS, |
281 |
"Unexpected headers for machine:\n" |
|
282 |
"wanted: %s" |
|
283 |
"got: %s" % (MACHINE_TAB_HEADERS, header)) |
|
979.3.8
by Horacio Durán
Added missing return |
284 |
normalized = dict(zip((AGENT_STATE_KEY, AGENT_VERSION_KEY, |
1025.1.2
by Aaron Bentley
Fix lint. |
285 |
DNS_NAME_KEY, INSTANCE_ID_KEY, |
286 |
SERIES_KEY, HARDWARE_KEY), |
|
287 |
nitems[1:])) |
|
979.3.8
by Horacio Durán
Added missing return |
288 |
return nitems[0], normalized |
979.3.1
by Horacio Durán
Added CI tests for status. |
289 |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
290 |
def _normalize_units(self, header, items): |
979.3.1
by Horacio Durán
Added CI tests for status. |
291 |
eid, wlstate, astate, version, machine, paddress = items[:6] |
292 |
message = " ".join(items[6:]) |
|
293 |
wlstatus = {"current": wlstate, "message": message, |
|
294 |
"since": "bogus date"} |
|
295 |
astatus = {"current": astate, "message": "", "since": "bogus date"} |
|
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
296 |
self.tc.assertEqual(header, UNIT_TAB_HEADERS, |
297 |
"Unexpected headers for unit.\n" |
|
298 |
"wanted: %s" |
|
299 |
"got: %s" % (UNIT_TAB_HEADERS, header)) |
|
979.3.8
by Horacio Durán
Added missing return |
300 |
normalized = dict(zip((WORKLOAD_STATUS_KEY, AGENT_STATUS_KEY, |
979.3.3
by Horacio Durán
Proper python formatting. |
301 |
AGENT_VERSION_KEY, MACHINE_KEY, |
302 |
PUBLIC_ADDRESS_KEY), |
|
1025.1.2
by Aaron Bentley
Fix lint. |
303 |
(wlstatus, astatus, version, machine, paddress))) |
979.3.1
by Horacio Durán
Added CI tests for status. |
304 |
|
979.3.8
by Horacio Durán
Added missing return |
305 |
return eid, normalized |
306 |
||
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
307 |
def _normalize_services(self, header, items): |
979.3.1
by Horacio Durán
Added CI tests for status. |
308 |
name, status, exposed, charm = items |
1031.1.3
by Curtis Hovey
Use tc as a class attribute instead of a decorator to be passed arround. |
309 |
self.tc.assertEqual(header, SERVICE_TAB_HEADERS, |
310 |
"Unexpected headers for service.\n" |
|
311 |
"wanted: %s" |
|
312 |
"got: %s" % (SERVICE_TAB_HEADERS, header)) |
|
979.3.8
by Horacio Durán
Added missing return |
313 |
normalized = dict(zip((CHARM_KEY, EXPOSED_KEY, SERVICE_STATUS_KEY), |
979.3.3
by Horacio Durán
Proper python formatting. |
314 |
(charm, exposed == "true", {"current": status, |
315 |
"message": ""}))) |
|
979.3.8
by Horacio Durán
Added missing return |
316 |
return name, normalized |
979.3.1
by Horacio Durán
Added CI tests for status. |
317 |
|
318 |
def _parse(self): |
|
319 |
section = re.compile("^\[(\w*)\]") |
|
979.3.3
by Horacio Durán
Proper python formatting. |
320 |
base = {"environment": "not provided"} |
979.3.1
by Horacio Durán
Added CI tests for status. |
321 |
current_parent = "" |
322 |
current_headers = [] |
|
323 |
prev_was_section = False |
|
324 |
for line in self._tabular.splitlines(): |
|
325 |
# parse section
|
|
326 |
is_section = section.findall(line) |
|
327 |
if len(is_section) == 1: |
|
328 |
current_parent = is_section[0].lower() |
|
329 |
if current_parent != "units": |
|
979.3.3
by Horacio Durán
Proper python formatting. |
330 |
base[current_parent] = {} |
979.3.1
by Horacio Durán
Added CI tests for status. |
331 |
prev_was_section = True |
332 |
continue
|
|
333 |
# parse headers
|
|
334 |
if prev_was_section: |
|
335 |
prev_was_section = False |
|
336 |
current_headers = line.split() |
|
337 |
continue
|
|
338 |
||
339 |
# parse content
|
|
979.3.3
by Horacio Durán
Proper python formatting. |
340 |
if current_parent == "" or current_headers == []: |
979.3.1
by Horacio Durán
Added CI tests for status. |
341 |
raise ErrMalformedStatus("Tabular status is malformed") |
342 |
items = line.split() |
|
343 |
||
344 |
# separation line
|
|
345 |
if len(items) == 0: |
|
346 |
continue
|
|
347 |
||
348 |
normalize = None |
|
349 |
if current_parent == "services": |
|
350 |
normalize = self._normalize_services |
|
351 |
elif current_parent == "units": |
|
352 |
normalize = self._normalize_units |
|
353 |
elif current_parent == "machines": |
|
354 |
normalize = self._normalize_machines |
|
355 |
||
356 |
if not normalize: |
|
357 |
raise ErrUntestedStatusOutput("%s is not an expected tabular" |
|
979.3.3
by Horacio Durán
Proper python formatting. |
358 |
" status section" % |
359 |
current_parent) |
|
979.3.1
by Horacio Durán
Added CI tests for status. |
360 |
k, v = normalize(current_headers, items) |
361 |
if current_parent == "units": |
|
362 |
base.setdefault("services", dict()) |
|
363 |
service = k.split("/")[0] |
|
364 |
base["services"][service].setdefault("units", dict()) |
|
365 |
base["services"][service]["units"][k] = v |
|
366 |
else: |
|
367 |
base[current_parent][k] = v |
|
368 |
self.store(base) |