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())
26 # Much faster and lighter library (C implementation)
27 from xml.etree import cElementTree as ElementTree
29 from xml.etree import ElementTree
31 import dateutil.parser
33 from wsag_model import Agreement
34 from wsag_model import Template
35 from wsag_model import Violation
36 from wsag_model import Provider
37 from wsag_model import Enforcement
40 def convertfile(converter, f):
41 """Reads and converts a xml file
44 :param Converter converter:
45 :param str f: file to read
47 tree = ElementTree.parse(f)
48 result = converter.convert(tree.getroot())
52 def convertstring(converter, string):
56 :param Converter converter:
57 :param str string: contains the xml to convert
59 root = ElementTree.fromstring(string)
60 result = converter.convert(root)
64 class Converter(object):
67 """Base class for converters
71 def convert(self, xmlroot):
72 """Converts the given xml in an object
74 :rtype : Object that represents the xml
75 :param Element xmlroot: root element of xml to convert.
80 class ListConverter(Converter):
81 def __init__(self, innerconverter):
82 super(ListConverter, self).__init__()
83 self.innerconverter = innerconverter
85 def convert(self, xmlroot):
88 # Converter for the old xml structure
89 # for item in xmlroot.find("items"): # loop through "items" children
90 # inner = self.innerconverter.convert(item)
91 # result.append(inner)
94 for item in xmlroot: # loop through children
95 inner = self.innerconverter.convert(item)
100 class ProviderConverter(Converter):
101 """Converter for a provider.
105 <uuid>1ad9acb9-8dbc-4fe6-9a0b-4244ab6455da</uuid>
106 <name>Provider2</name>
114 super(ProviderConverter, self).__init__()
116 def convert(self, xmlroot):
118 result.uuid = xmlroot.find("uuid").text
119 result.name = xmlroot.find("name").text
123 class EnforcementConverter(Converter):
124 """Converter for an Enforcement job.
128 <agreement_id>agreement03</agreement_id>
129 <enabled>false</enabled>
133 wsag_model.Enforcement
137 super(EnforcementConverter, self).__init__()
139 def convert(self, xmlroot):
140 result = Enforcement()
141 result.agreement_id = xmlroot.find("agreement_id").text
142 result.enabled = xmlroot.find("enabled").text
146 class ViolationConverter(Converter):
147 """Converter for a violation.
151 <uuid>1d94627e-c318-41ba-9c45-42c95b67cc32</uuid>
152 <contract_uuid>26e5d5b6-f5a1-4eb3-bc91-606e8f24fb09</contract_uuid>
153 <service_name>servicename1</service_name>
154 <service_scope>test1</service_scope>
155 <metric_name>UpTime</metric_name>
156 <datetime>2014-07-17T09:32:00+02:00</datetime>
157 <actual_value>0.0</actual_value>
164 super(ViolationConverter, self).__init__()
166 def convert(self, xmlroot):
168 result.uuid = xmlroot.find("uuid").text
169 result.contract_uuid = xmlroot.find("contract_uuid").text
170 result.service_name = xmlroot.find("service_name").text
171 result.service_scope = xmlroot.find("service_scope").text
172 result.metric_name = xmlroot.find("metric_name").text
173 result.actual_value = xmlroot.find("actual_value").text
174 dt_str = xmlroot.find("datetime").text
175 result.datetime = dateutil.parser.parse(dt_str)
180 class AgreementConverter(Converter):
182 """Converter for an ws-agreement agreement or template.
184 super(AgreementConverter, self).__init__()
186 "wsag": "http://www.ggf.org/namespaces/ws-agreement",
187 "sla": "http://sla.atos.eu",
189 self.agreement_tags = (
190 "{{{}}}Agreement".format(self._namespaces["wsag"]),
192 self.template_tags = (
193 "{{{}}}Template".format(self._namespaces["wsag"]),
196 def convert(self, xmlroot):
198 :param Element xmlroot: root element of xml to convert.
199 :rtype: wsag_model.Agreement
201 if xmlroot.tag in self.agreement_tags:
203 result.agreement_id = xmlroot.attrib["AgreementId"]
204 elif xmlroot.tag in self.template_tags:
206 result.template_id = xmlroot.attrib["TemplateId"]
208 raise ValueError("Not valid root element name: " + xmlroot.tag)
210 context = xmlroot.find("wsag:Context", self._namespaces)
211 result.context = self._parse_context(context)
213 terms = xmlroot.find("wsag:Terms/wsag:All", self._namespaces)
215 properties = terms.findall("wsag:ServiceProperties", self._namespaces)
216 result.variables = self._parse_properties(properties)
218 guarantees = terms.findall("wsag:GuaranteeTerm", self._namespaces)
219 result.guaranteeterms = self._parse_guarantees(guarantees)
223 def _parse_context(self, element):
224 nss = self._namespaces
225 result = Agreement.Context()
227 result.template_id = self._find_text(element, "wsag:TemplateId")
228 result.expirationtime = self._find_text(element, "wsag:ExpirationTime")
230 service_elem = element.find("sla:Service", nss)
232 service_elem.text if service_elem is not None else "<servicename>"
234 initiator = self._find_text(element, "wsag:AgreementInitiator")
235 responder = self._find_text(element, "wsag:AgreementResponder")
236 serviceprovider_elem = self._find_text(element, "wsag:ServiceProvider")
239 # Deloop the initiator-responder indirection.
241 if serviceprovider_elem == "AgreementResponder":
244 elif serviceprovider_elem == "AgreementInitiator":
249 "Invalid value for wsag:ServiceProvider : " +
250 serviceprovider_elem)
252 result.initiator = initiator
253 result.responder = responder
254 result.provider = provider
255 result.consumer = consumer
259 def _parse_property(self, element, servicename):
260 nss = self._namespaces
262 key = _get_attribute(element, "Name")
263 value = Agreement.Property()
264 value.servicename = servicename
266 value.metric = _get_attribute(element, "Metric")
267 value.location = element.find("wsag:Location", nss).text
271 def _parse_properties(self, elements):
273 nss = self._namespaces
274 for element in elements:
275 servicename = _get_attribute(element, "ServiceName")
276 for var in element.findall("wsag:Variables/wsag:Variable", nss):
277 key, value = self._parse_property(var, servicename)
282 def _parse_guarantee_scope(self, element):
283 result = Agreement.GuaranteeTerm.GuaranteeScope()
284 result.servicename = _get_attribute(element, "ServiceName")
285 result.scope = element.text
288 def _parse_guarantee_scopes(self, elements):
290 for scope in elements:
291 result.append(self._parse_guarantee_scope(scope))
294 def _parse_guarantee(self, element):
295 nss = self._namespaces
297 result = Agreement.GuaranteeTerm()
298 name = _get_attribute(element, "Name")
300 scopes = element.findall("wsag:ServiceScope", nss)
301 result.scopes = self._parse_guarantee_scopes(scopes)
303 kpitarget = element.find(
304 "wsag:ServiceLevelObjective/wsag:KPITarget", nss)
305 slo = Agreement.GuaranteeTerm.ServiceLevelObjective()
306 result.servicelevelobjective = slo
307 slo.kpiname = kpitarget.find("wsag:KPIName", nss).text
308 slo.customservicelevel = kpitarget.find(
309 "wsag:CustomServiceLevel", nss).text
313 def _parse_guarantees(self, elements):
316 for element in elements:
317 key, value = self._parse_guarantee(element)
321 def _find_text(self, src, path):
322 """Returns the inner text of the element located in path from the src
323 element; None if no elements were found.
330 text = _find_text(root, "wsag:Context/ExpirationTime")
332 dst = src.find(path, self._namespaces)
338 def _get_attribute(element, attrname):
340 Get attribute from an element.
342 Wrapper over Element.attrib, as this doesn't fallback to the element
343 namespace if the attribute is qnamed and the requested attribute name
347 <ns:elem attr1="value1" ns:attr2="value2"/>
349 _get_attribute(elem, "attr1") -> value1
350 _get_attribute(elem, "attr2") -> value2
351 _get_attribute(elem, "{uri}:attr1") -> Error
352 _get_attribute(elem, "{uri}:attr2") -> value2
354 isns = (attrname[0] == '{')
357 # Handle qnamed request:
358 # attrname = {uri}name
361 return element.attrib[attrname]
364 # Handle non-qnamed request and non-qnamed actual_attr
368 if attrname in element.attrib:
369 return element.attrib[attrname]
372 # Handle non-qnamed request but qnamed actualAttr
374 # actual_attr = {uri}name
376 tag_uri = element.tag[0: element.tag.find('}') + 1]
377 return element.attrib[tag_uri + attrname]