1 # -*- coding: utf-8 -*-
3 """Converts from XML to objects for ws-agreement agreements/templates or any
4 other xml returned by SLA Manager.
6 This module offers a set of converters from xml formats returned by SLA Manager
7 to a more-friendly POJO instances.
9 The converters are designed to be pluggable: see ListConverter.
14 c = ListConverter(AnyOtherConverter())
16 convertstring(c, "<?xml ... </>")
18 convertfile(c, "file.xml")
20 root = ElementTree.parse("file.xml")
21 c.convert(root.getroot())
25 from xml.etree import ElementTree
26 from xml.etree.ElementTree import Element
27 import dateutil.parser
29 from wsag_model import Agreement
30 from wsag_model import Template
31 from wsag_model import Violation
32 from wsag_model import Provider
33 from wsag_model import Enforcement
36 def convertfile(converter, f):
37 """Reads and converts a xml file
40 :param Converter converter:
41 :param str f: file to read
43 tree = ElementTree.parse(f)
44 result = converter.convert(tree.getroot())
48 def convertstring(converter, string):
52 :param Converter converter:
53 :param str string: contains the xml to convert
55 root = ElementTree.fromstring(string)
56 result = converter.convert(root)
60 class Converter(object):
63 """Base class for converters
67 def convert(self, xmlroot):
68 """Converts the given xml in an object
70 :rtype : Object that represents the xml
71 :param Element xmlroot: root element of xml to convert.
76 class ListConverter(Converter):
77 def __init__(self, innerconverter):
78 super(ListConverter, self).__init__()
79 self.innerconverter = innerconverter
81 def convert(self, xmlroot):
84 for item in xmlroot.find("items"): # loop through "items" children
85 inner = self.innerconverter.convert(item)
90 class ProviderConverter(Converter):
91 """Converter for a provider.
95 <uuid>1ad9acb9-8dbc-4fe6-9a0b-4244ab6455da</uuid>
96 <name>Provider2</name>
104 super(ProviderConverter, self).__init__()
106 def convert(self, xmlroot):
108 result.uuid = xmlroot.find("uuid").text
109 result.name = xmlroot.find("name").text
113 class EnforcementConverter(Converter):
114 """Converter for an Enforcement job.
118 <agreement_id>agreement03</agreement_id>
119 <enabled>false</enabled>
123 wsag_model.Enforcement
127 super(EnforcementConverter, self).__init__()
129 def convert(self, xmlroot):
130 result = Enforcement()
131 result.agreement_id = xmlroot.find("agreement_id").text
132 result.enabled = xmlroot.find("enabled").text
135 class ViolationConverter(Converter):
136 """Converter for a violation.
140 <uuid>ce0e148f-dfac-4492-bb26-ad2e9a6965ec</uuid>
141 <contract_uuid>agreement04</contract_uuid>
142 <service_scope></service_scope>
143 <metric_name>Performance</metric_name>
144 <datetime>2014-01-14T11:28:22Z</datetime>
145 <actual_value>0.09555700123360344</actual_value>
152 super(ViolationConverter, self).__init__()
154 def convert(self, xmlroot):
156 result.uuid = xmlroot.find("uuid").text
157 result.contract_uuid = xmlroot.find("contract_uuid").text
158 result.service_scope = xmlroot.find("service_scope").text
159 result.metric_name = xmlroot.find("metric_name").text
160 result.actual_value = xmlroot.find("actual_value").text
161 dt_str = xmlroot.find("datetime").text
162 result.datetime = dateutil.parser.parse(dt_str)
166 class AgreementConverter(Converter):
168 """Converter for an ws-agreement agreement or template.
170 super(AgreementConverter, self).__init__()
172 "wsag": "http://www.ggf.org/namespaces/ws-agreement",
173 "sla": "http://sla.atos.eu",
174 "xifi": "http://sla.xifi.eu"
176 self.agreement_tags = (
177 "{{{}}}Agreement".format(self._namespaces["wsag"]),
179 self.template_tags = (
180 "{{{}}}Template".format(self._namespaces["wsag"]),
183 def convert(self, xmlroot):
185 :param Element xmlroot: root element of xml to convert.
186 :rtype: wsag_model.Agreement
188 if xmlroot.tag in self.agreement_tags:
190 result.agreement_id = xmlroot.attrib["AgreementId"]
191 elif xmlroot.tag in self.template_tags:
193 result.template_id = xmlroot.attrib["TemplateId"]
195 raise ValueError("Not valid root element name: " + xmlroot.tag)
197 context = xmlroot.find("wsag:Context", self._namespaces)
198 result.context = self._parse_context(context)
200 terms = xmlroot.find("wsag:Terms/wsag:All", self._namespaces)
202 properties = terms.findall("wsag:ServiceProperties", self._namespaces)
203 result.variables = self._parse_properties(properties)
205 guarantees = terms.findall("wsag:GuaranteeTerm", self._namespaces)
206 result.guaranteeterms = self._parse_guarantees(guarantees)
210 def _parse_context(self, element):
211 nss = self._namespaces
212 result = Agreement.Context()
214 result.template_id = self._find_text(element, "wsag:TemplateId")
215 result.expirationtime = self._find_text(element, "wsag:ExpirationTime")
217 service_elem = element.find("sla:Service", nss)
219 service_elem.text if service_elem is not None else "<servicename>"
221 initiator = self._find_text(element, "wsag:AgreementInitiator")
222 responder = self._find_text(element, "wsag:AgreementResponder")
223 serviceprovider_elem = self._find_text(element, "wsag:ServiceProvider")
226 # Deloop the initiator-responder indirection.
228 if serviceprovider_elem == "AgreementResponder":
231 elif serviceprovider_elem == "AgreementInitiator":
236 "Invalid value for wsag:ServiceProvider : " +
237 serviceprovider_elem)
239 result.initiator = initiator
240 result.responder = responder
241 result.provider = provider
242 result.consumer = consumer
246 def _parse_property(self, element, servicename):
247 nss = self._namespaces
249 key = _get_attribute(element, "Name")
250 value = Agreement.Property()
251 value.servicename = servicename
253 value.metric = _get_attribute(element, "Metric")
254 value.location = element.find("wsag:Location", nss).text
258 def _parse_properties(self, elements):
260 nss = self._namespaces
261 for element in elements:
262 servicename = _get_attribute(element, "ServiceName")
263 for var in element.findall("wsag:Variables/wsag:Variable", nss):
264 key, value = self._parse_property(var, servicename)
269 def _parse_guarantee_scope(self, element):
270 result = Agreement.GuaranteeTerm.GuaranteeScope()
271 result.servicename = _get_attribute(element, "ServiceName")
272 result.scope = element.text
275 def _parse_guarantee_scopes(self, elements):
277 for scope in elements:
278 result.append(self._parse_guarantee_scope(scope))
281 def _parse_guarantee(self, element):
282 nss = self._namespaces
284 result = Agreement.GuaranteeTerm()
285 name = _get_attribute(element, "Name")
287 scopes = element.findall("wsag:ServiceScope", nss)
288 result.scopes = self._parse_guarantee_scopes(scopes)
290 kpitarget = element.find(
291 "wsag:ServiceLevelObjective/wsag:KPITarget", nss)
292 slo = Agreement.GuaranteeTerm.ServiceLevelObjective()
293 result.servicelevelobjective = slo
294 slo.kpiname = kpitarget.find("wsag:KPIName", nss).text
295 slo.customservicelevel = kpitarget.find(
296 "wsag:CustomServiceLevel", nss).text
300 def _parse_guarantees(self, elements):
303 for element in elements:
304 key, value = self._parse_guarantee(element)
308 def _find_text(self, src, path):
309 """Returns the inner text of the element located in path from the src
310 element; None if no elements were found.
317 text = _find_text(root, "wsag:Context/ExpirationTime")
319 dst = src.find(path, self._namespaces)
325 def _get_attribute(element, attrname):
327 Get attribute from an element.
329 Wrapper over Element.attrib, as this doesn't fallback to the element
330 namespace if the attribute is qnamed and the requested attribute name
334 <ns:elem attr1="value1" ns:attr2="value2"/>
336 _get_attribute(elem, "attr1") -> value1
337 _get_attribute(elem, "attr2") -> value2
338 _get_attribute(elem, "{uri}:attr1") -> Error
339 _get_attribute(elem, "{uri}:attr2") -> value2
341 isns = (attrname[0] == '{')
344 # Handle qnamed request:
345 # attrname = {uri}name
348 return element.attrib[attrname]
351 # Handle non-qnamed request and non-qnamed actual_attr
355 if attrname in element.attrib:
356 return element.attrib[attrname]
359 # Handle non-qnamed request but qnamed actualAttr
361 # actual_attr = {uri}name
363 tag_uri = element.tag[0: element.tag.find('}') + 1]
364 return element.attrib[tag_uri + attrname]